summaryrefslogtreecommitdiff
path: root/graphics/pgf/base/tex/generic/graphdrawing/lua
diff options
context:
space:
mode:
Diffstat (limited to 'graphics/pgf/base/tex/generic/graphdrawing/lua')
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/LUA_CODING_STYLE27
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf.lua72
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd.lua72
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/bindings.lua26
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/bindings/Binding.lua265
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/bindings/BindingToPGF.lua374
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/circular.lua22
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/circular/Tantau2012.lua153
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/circular/doc.lua145
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/circular/library.lua30
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/control.lua26
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/control/Anchoring.lua104
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/control/ComponentAlign.lua540
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/control/ComponentDirection.lua58
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/control/ComponentDistance.lua108
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/control/ComponentOrder.lua105
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/control/Components.lua127
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/control/Distances.lua454
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/control/FineTune.lua164
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/control/LayoutPipeline.lua1347
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/control/NodeAnchors.lua175
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/control/Orientation.lua303
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/control/Sublayouts.lua536
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/control/doc.lua245
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/control/library.lua196
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/deprecated/Cluster.lua65
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/deprecated/Edge.lua358
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/deprecated/Graph.lua396
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/deprecated/Iterators.lua91
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/deprecated/Node.lua280
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/deprecated/Vector.lua256
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc.lua124
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/energybased.lua19
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/energybased/FMMMLayout.lua129
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/energybased/FastMultipoleEmbedder.lua51
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/energybased/GEMLayout.lua103
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/energybased/MultilevelLayout.lua33
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/energybased/SpringEmbedderFR.lua53
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/energybased/SpringEmbedderFRExact.lua57
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/energybased/SpringEmbedderKK.lua62
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/energybased/multilevelmixer/BarycenterPlacer.lua33
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/energybased/multilevelmixer/CirclePlacer.lua45
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/energybased/multilevelmixer/EdgeCoverMerger.lua33
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/energybased/multilevelmixer/IndependentSetMerger.lua33
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/energybased/multilevelmixer/LocalBiconnectedMerger.lua33
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/energybased/multilevelmixer/MatchingMerger.lua33
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/energybased/multilevelmixer/MedianPlacer.lua27
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/energybased/multilevelmixer/RandomMerger.lua33
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/energybased/multilevelmixer/RandomPlacer.lua33
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/energybased/multilevelmixer/SolarMerger.lua39
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/energybased/multilevelmixer/SolarPlacer.lua27
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/energybased/multilevelmixer/ZeroPlacer.lua33
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/layered.lua19
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/layered/BarycenterHeuristic.lua23
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/layered/CoffmanGrahamRanking.lua35
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/layered/DfsAcyclicSubgraph.lua27
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/layered/FastHierarchyLayout.lua99
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/layered/FastSimpleHierarchyLayout.lua99
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/layered/GreedyCycleRemoval.lua27
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/layered/GreedyInsertHeuristic.lua23
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/layered/LongestPathRanking.lua57
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/layered/MedianHeuristic.lua24
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/layered/OptimalRanking.lua35
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/layered/SiftingHeuristic.lua34
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/layered/SplitHeuristic.lua24
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/layered/SugiyamaLayout.lua113
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/misclayout.lua19
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/misclayout/BalloonLayout.lua48
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/misclayout/CircularLayout.lua95
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/module/AcyclicSubgraphModule.lua18
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/module/HierarchyLayoutModule.lua17
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/module/InitialPlacer.lua17
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/module/MultilevelBuilder.lua17
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/module/RankingModule.lua17
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/module/TwoLayerCrossMin.lua17
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/planarity.lua19
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/planarity/PlanarizationLayout.lua66
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/examples.lua26
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/examples/ASCIIDisplayer.lua32
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/examples/BindingToASCII.lua87
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/examples/SimpleDemo.lua82
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/examples/SimpleEdgeDemo.lua144
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/examples/SimpleHuffman.lua287
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/examples/example_graph_for_ascii_displayer.txt20
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/examples/library.lua30
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/experimental/evolving/GraphAnimationCoordination.lua638
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/experimental/evolving/GreedyTemporalCycleRemoval.lua177
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/experimental/evolving/Skambath2016.lua875
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/experimental/evolving/Supergraph.lua571
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/experimental/evolving/SupergraphVertexSplitOptimization.lua196
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/experimental/evolving/TimeSpec.lua62
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/experimental/evolving/doc.lua120
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/experimental/evolving/layered.lua107
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/experimental/evolving/library.lua33
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/force.lua26
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/force/CoarseGraph.lua435
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/force/ControlCoarsening.lua148
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/force/ControlDeclare.lua41
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/force/ControlElectric.lua105
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/force/ControlIteration.lua136
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/force/ControlSprings.lua59
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/force/ControlStart.lua41
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/force/QuadTree.lua280
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/force/SpringElectricalHu2006.lua633
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/force/SpringElectricalLayouts.lua54
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/force/SpringElectricalWalshaw2000.lua520
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/force/SpringHu2006.lua386
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/force/SpringLayouts.lua36
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/force/jedi/algorithms/FruchtermanReingold.lua124
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/force/jedi/algorithms/HuSpringElectricalFW.lua95
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/force/jedi/algorithms/SimpleSpring.lua74
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/force/jedi/algorithms/SocialGravityCloseness.lua129
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/force/jedi/algorithms/SocialGravityDegree.lua183
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/force/jedi/base/CoarseGraphFW.lua264
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/force/jedi/base/ForceController.lua489
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/force/jedi/base/ForceTemplate.lua44
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/force/jedi/base/InitialTemplate.lua64
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/force/jedi/base/PathLengthsFW.lua174
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/force/jedi/base/Preprocessing.lua122
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/force/jedi/doc.lua377
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/force/jedi/forcetypes/ForceAbsoluteValue.lua94
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/force/jedi/forcetypes/ForceCanvasDistance.lua201
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/force/jedi/forcetypes/ForceCanvasPosition.lua117
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/force/jedi/forcetypes/ForceGraphDistance.lua205
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/force/jedi/forcetypes/ForcePullToGrid.lua123
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/force/jedi/forcetypes/ForcePullToPoint.lua119
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/force/jedi/initialpositioning/CircularInitialPositioning.lua60
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/force/jedi/initialpositioning/GridInitialPositioning.lua60
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/force/jedi/initialpositioning/RandomInitialPositioning.lua49
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/force/jedi/library.lua115
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/force/library.lua126
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/interface.lua26
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/interface/InterfaceCore.lua191
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/interface/InterfaceToAlgorithms.lua968
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/interface/InterfaceToC.lua78
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/interface/InterfaceToDisplay.lua1002
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/interface/Scope.lua92
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/layered.lua159
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/layered/CrossingMinimizationGansnerKNV1993.lua342
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/layered/CycleRemovalBergerS1990a.lua74
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/layered/CycleRemovalBergerS1990b.lua76
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/layered/CycleRemovalEadesLS1993.lua124
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/layered/CycleRemovalGansnerKNV1993.lua52
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/layered/EdgeRoutingGansnerKNV1993.lua22
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/layered/NetworkSimplex.lua898
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/layered/NodePositioningGansnerKNV1993.lua154
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/layered/NodeRankingGansnerKNV1993.lua159
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/layered/NodeRankingMinimumHeight.lua47
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/layered/Ranking.lua291
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/layered/Sugiyama.lua476
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/layered/crossing_minimization.lua81
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/layered/cycle_removal.lua136
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/layered/edge_routing.lua49
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/layered/library.lua94
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/layered/node_positioning.lua55
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/layered/node_ranking.lua72
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/lib.lua435
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/lib/Bezier.lua160
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/lib/DepthFirstSearch.lua125
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/lib/Direct.lua95
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/lib/Event.lua98
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/lib/LookupTable.lua111
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/lib/PathLengths.lua209
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/lib/PriorityQueue.lua342
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/lib/Simplifiers.lua279
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/lib/Stack.lua61
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/lib/Storage.lua110
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/lib/Transform.lua120
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/model.lua26
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/model/Arc.lua653
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/model/Collection.lua208
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/model/Coordinate.lua306
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/model/Digraph.lua849
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/model/Edge.lua209
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/model/Hyperedge.lua56
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/model/Path.lua1278
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/model/Path_arced.lua316
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/model/Vertex.lua296
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/model/library.lua15
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/ogdf.lua26
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/ogdf/library.lua33
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/pedigrees.lua21
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/pedigrees/Koerner2015.lua163
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/pedigrees/library.lua21
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/phylogenetics.lua21
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/phylogenetics/AuthorDefinedPhylogeny.lua77
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/phylogenetics/BalancedMinimumEvolution.lua593
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/phylogenetics/BalancedNearestNeighbourInterchange.lua372
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/phylogenetics/DistanceMatrix.lua451
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/phylogenetics/Maeusle2012.lua778
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/phylogenetics/PhylogeneticTree.lua91
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/phylogenetics/SokalMichener1958.lua261
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/phylogenetics/library.lua35
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/planar.lua6
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/planar/BoyerMyrvold2004.lua678
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/planar/Embedding.lua788
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/planar/LinkedList.lua88
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/planar/List.lua49
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/planar/PDP.lua576
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/planar/PlanarLayout.lua159
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/planar/ShiftMethod.lua128
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/planar/library.lua2
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/planar/parameters.lua144
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/routing.lua22
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/routing/Hints.lua100
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/routing/NecklaceRouting.lua90
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/routing/library.lua29
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/tools/make_gd_wrap.lua183
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/trees.lua21
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/trees/ChildSpec.lua226
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/trees/ReingoldTilford1981.lua211
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/trees/SpanningTreeComputation.lua646
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/trees/doc.lua340
-rw-r--r--graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/trees/library.lua35
214 files changed, 38476 insertions, 0 deletions
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/LUA_CODING_STYLE b/graphics/pgf/base/tex/generic/graphdrawing/lua/LUA_CODING_STYLE
new file mode 100644
index 0000000000..295a8b2ed1
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/LUA_CODING_STYLE
@@ -0,0 +1,27 @@
+Lua coding style for the graphdrawing library
+=============================================
+
+General naming rules and indentation:
+
+ * 2 spaces indentation
+ * variable_names_with_underscores
+ * namespaced.low_level_functions_with_underscores(foo, bar)
+ * CamelCaseClassNames
+ * CamelCaseClassNames.nameOfStaticFunctionLikeNewOrClone(foo, bar)
+ * CamelCaseClass:functionNamesLikeThis(foo, bar)
+ * never use global variables
+ * Use . for static functions and : for member functions
+
+LuaDoc comments:
+
+ ---
+ -- A function to do this and that.
+ --
+ -- @param first Description of the first parameter.
+ -- @param second Description of the second parameter.
+ --
+ -- @return This function returns something.
+ --
+ function myClass:myFunction(first, second)
+ ...
+ end
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf.lua
new file mode 100644
index 0000000000..b0a3247616
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf.lua
@@ -0,0 +1,72 @@
+-- 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$
+
+
+---
+-- The |pgf| namespace lies in the global namespace. It is the only
+-- global table defined by \pgfname. The whole graph drawing system,
+-- in turn, lies in the table |pgf.gd|.
+
+pgf = {}
+
+
+
+-- Forward
+local tostring_table
+
+---
+-- Writes debug info on the \TeX\ output, separating the parameters
+-- by spaces. The debug information will include a complete traceback
+-- of the stack, allowing you to see ``where you are'' inside the Lua
+-- program.
+--
+-- Note that this function resides directly in the |pgf| table. The
+-- reason for this is that you can ``always use it'' since |pgf| is
+-- always available in the global name space.
+--
+-- @param ... List of parameters to write to the \TeX\ output.
+
+function pgf.debug(...)
+ local stacktrace = debug.traceback("",2)
+ texio.write_nl(" ")
+ texio.write_nl("Debug called for: ")
+ -- this is to even print out nil arguments in between
+ local args = {...}
+ for i = 1, #args do
+ if i ~= 1 then texio.write(", ") end
+ texio.write(tostring_table(args[i], "", 5))
+ end
+ texio.write_nl('')
+ for w in string.gmatch(stacktrace, "/.-:.-:.-%c") do
+ texio.write('by ', string.match(w,".*/(.*)"))
+ end
+end
+
+
+-- Helper function
+
+function tostring_table(t, prefix, depth)
+ if type(t) ~= "table" or (getmetatable(t) and getmetatable(t).__tostring) or depth <= 0 then
+ return type(t) == "string" and ('"' .. t .. '"') or tostring(t)
+ else
+ local r = "{\n"
+ for k,v in pairs(t) do
+ r = r .. prefix .. " " .. tostring(k) .. "=" ..
+ (v==t and "self" or tostring_table(v, prefix .. " ", depth-1)) .. ",\n"
+ end
+ return r .. prefix .. "}"
+ end
+end
+
+
+
+
+return pgf \ No newline at end of file
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd.lua
new file mode 100644
index 0000000000..20d8acafa5
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd.lua
@@ -0,0 +1,72 @@
+-- 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$
+
+
+
+
+-- Declare the gd namespace
+
+local gd = {}
+require("pgf").gd = gd
+
+
+
+
+
+
+
+-- Deprecated:
+--
+--
+-- Helping function for creating new algorithm classes
+--
+-- This function creates a new algorithm class. This class will have a
+-- new method, that takes a graph and, optionally, a parent algorithm
+-- as inputs. They will be stored in the "graph" and "parent_algorithm"
+-- fields, respectively.
+--
+-- @param info This table is used to configure the new class. It has
+-- the following fields: First, there is the "properties" table. If
+-- this table is present, it will be used as the default table. Second,
+-- it can have a graph_parameters table. This table will be used in the
+-- constructor to preload graph parameters from the pgf layer. For
+-- this, each entry of the table should be of the form
+--
+-- key = 'string'
+--
+-- What happens is that upon the creation of a new algorithm object,
+-- for each key we lookup the graph option 'string' and
+-- store its value in the key of the new algorithm object.
+--
+-- @return A table that is a class with a new function setup.
+
+function gd.new_algorithm_class (class)
+ class.__index = class
+ class.new =
+ function (initial)
+
+ -- Create new object
+ local obj = {}
+ for k,v in pairs(initial) do
+ obj[k] = v
+ end
+ setmetatable(obj, class)
+
+ return obj
+ end
+ class.preconditions = {}
+ class.postconditions = {}
+
+ return class
+end
+
+
+return gd
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/bindings.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/bindings.lua
new file mode 100644
index 0000000000..410da3af45
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/bindings.lua
@@ -0,0 +1,26 @@
+-- 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$
+
+
+
+-- Imports
+
+require "pgf"
+require "pgf.gd"
+
+
+-- Declare namespace
+pgf.gd.bindings = {}
+
+
+-- Done
+
+return pgf.gd.bindings \ No newline at end of file
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/bindings/Binding.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/bindings/Binding.lua
new file mode 100644
index 0000000000..6ffb4168f5
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/bindings/Binding.lua
@@ -0,0 +1,265 @@
+-- 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$
+
+
+-- Imports
+local Storage = require "pgf.gd.lib.Storage"
+
+
+---
+-- This class provides a (non-functional) default implementation of a
+-- binding between a display layer and the algorithm layer. Subclasses
+-- must implement most of the member functions declared in this class.
+--
+-- A instance of a subclass of this class encapsulates the complete
+-- implementation of all specific code needed for the communication
+-- between the display layer and the graph drawing engine.
+--
+-- Note that you never call the methods of a |Binding| object
+-- directly, neither from the display layer nor from the algorithm
+-- layer. Instead, you use the more powerful and more easy to use
+-- functions from |InterfaceToDisplay| and
+-- |InterfaceToAlgorithms|. They call the appropriate |Binding|
+-- methods internally.
+--
+-- Because of the above, in order to use the graph drawing system
+-- inside a new display layer, you need to subclass |Binding| and
+-- implement all the functions. Then you need to write the display
+-- layer in such a way that it calls the appropriate functions from
+-- |InterfaceToDisplay|.
+--
+-- @field storage A |Storage| storing the information passed from the
+-- display layer. The interpretation of this left to the actual
+-- binding.
+
+local Binding = {
+ storage = Storage.newTableStorage ()
+}
+Binding.__index = Binding
+
+-- Namespace
+require("pgf.gd.bindings").Binding = Binding
+
+
+
+
+
+--
+-- This method gets called whenever the graph drawing coroutine should
+-- be resumed. First, the binding layer should ask the display layer
+-- to execute the |code|, then, after this is done, the function
+-- |InterfaceToDisplay.resumeGraphDrawingCoroutine| should be called
+-- by this function.
+--
+-- @param code Some code to be executed by the display layer.
+
+function Binding:resumeGraphDrawingCoroutine(code)
+ -- Does nothing by default
+end
+
+
+---
+-- Declare a new key. This callback is called by |declare|. It is the job
+-- of the display layer to make the parameter |t.key| available to the
+-- parsing process. Furthermore, if |t.initial| is not |nil|, the
+-- display layer must convert it into a value that is stored as the
+-- initial value and call |InterfaceToDisplay.setOptionInitial|.
+--
+-- @param t See |InterfaceToAlgorithms.declare| for details.
+
+function Binding:declareCallback(t)
+ -- Does nothing by default
+end
+
+
+
+
+-- Rendering
+
+---
+-- This function and, later on, |renderStop| are called whenever the
+-- rendering of a laid-out graph starts or stops. See
+-- |InterfaceToDisplay.render| for details.
+
+function Binding:renderStart()
+ -- Does nothing by default
+end
+
+---
+-- See |renderStart|.
+
+function Binding:renderStop()
+ -- Does nothing by default
+end
+
+
+
+
+
+---
+-- This function and the corresponding |...Stop...| functions are
+-- called whenever a collection kind should be rendered. See
+-- |InterfaceToDisplay.render_collections| for details.
+--
+-- @param kind The kind (a string).
+-- @param layer The kind's layer (a number).
+
+function Binding:renderCollectionStartKind(kind, layer)
+ -- Does nothing by default
+end
+
+
+---
+-- The counterpart to |renderCollectionStartKind|.
+--
+-- @param kind The kind.
+-- @param layer The kind's layer.
+
+function Binding:renderCollectionStopKind(kind, layer)
+ -- Does nothing by default
+end
+
+
+---
+-- Renders a single collection, see |renderCollectionStartKind| for
+-- details.
+--
+-- @param collection The collection object.
+
+function Binding:renderCollection(collection)
+ -- Does nothing by default
+end
+
+
+
+---
+-- This function and the corresponding |...Stop...| functions are
+-- called whenever a vertex should be rendered. See
+-- |InterfaceToDisplay.render_vertices| for details.
+--
+
+function Binding:renderVerticesStart()
+ -- Does nothing by default
+end
+
+
+---
+-- The counterpart to |renderVerticesStop|.
+--
+
+function Binding:renderVerticesStop()
+ -- Does nothing by default
+end
+
+
+---
+-- Renders a single vertex, see |renderVertexStartKind| for
+-- details.
+--
+-- @param vertex The |Vertex| object.
+
+function Binding:renderVertex(vertex)
+ -- Does nothing by default
+end
+
+
+
+---
+-- This method is called by the interface to the display layer after
+-- the display layer has called |createVertex| to create a new
+-- vertex. After having done its internal bookkeeping, the interface
+-- calls this function to allow the binding to perform further
+-- bookkeeping on the node. Typically, this will be done using the
+-- information stored in |Binding.infos|.
+--
+-- @param v The vertex.
+
+function Binding:everyVertexCreation(v)
+ -- Does nothing by default
+end
+
+
+
+
+
+---
+-- This function and the corresponding |...Stop...| functions are
+-- called whenever an edge should be rendered. See
+-- |InterfaceToDisplay.render_edges| for details.
+--
+
+function Binding:renderEdgesStart()
+ -- Does nothing by default
+end
+
+
+---
+-- The counterpart to |renderEdgesStop|.
+--
+
+function Binding:renderEdgesStop()
+ -- Does nothing by default
+end
+
+
+---
+-- Renders a single vertex, see |renderEdgeStartKind| for
+-- details.
+--
+-- @param edge The |Edge| object.
+
+function Binding:renderEdge(edge)
+ -- Does nothing by default
+end
+
+
+---
+-- Like |everyVertexCreation|, only for edges.
+--
+-- @param e The edge.
+
+function Binding:everyEdgeCreation(e)
+ -- Does nothing by default
+end
+
+
+---
+-- Generate a new vertex. This method will be called when the
+-- \emph{algorithm} layer wishes to trigger the creation of a new
+-- vertex. This call will be made while an algorithm is running. It is
+-- now the job of the binding to cause the display layer to create the
+-- node. This is done by calling the |yield| method of the scope's
+-- coroutine.
+--
+-- @param init A table of initial values for the node. The following
+-- fields will be used:
+-- %
+-- \begin{itemize}
+-- \item |name| If present, this name will be given to the
+-- node. If not present, an internal name is generated. Note that,
+-- unless the node is a subgraph node, this name may not be the name
+-- of an already present node of the graph; in this case an error
+-- results.
+-- \item |shape| If present, a shape of the node.
+-- \item |generated_options| A table that is passed back to the
+-- display layer as a list of key--value pairs.
+-- \item |text| The text of the node, to be passed back to the
+-- higher layer. This is what should be displayed as the node's text.
+-- \end{itemize}
+
+function Binding:createVertex(init)
+ -- Does nothing by default
+end
+
+
+
+
+return Binding \ No newline at end of file
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/bindings/BindingToPGF.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/bindings/BindingToPGF.lua
new file mode 100644
index 0000000000..5324a1652e
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/bindings/BindingToPGF.lua
@@ -0,0 +1,374 @@
+-- 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$
+
+
+
+-- Imports
+local Storage = require "pgf.gd.lib.Storage"
+
+
+---
+-- This class, which is a subclass of |Binding|, binds the graph
+-- drawing system to the \pgfname\ display system by overriding (that
+-- is, implementing) the methods of the |Binding| class. As a typical
+-- example, consider the implementation of the function |renderVertex|:
+-- %
+--\begin{codeexample}[code only, tikz syntax=false]
+--function BindingToPGF:renderVertex(v)
+-- local info = assert(self.infos[v], "thou shalt not modify the syntactic digraph")
+-- tex.print(
+-- string.format(
+-- "\\pgfgdcallbackrendernode{%s}{%fpt}{%fpt}{%fpt}{%fpt}{%fpt}{%fpt}{%s}",
+-- 'not yet positionedPGFINTERNAL' .. v.name,
+-- info.x_min,
+-- info.x_max,
+-- info.y_min,
+-- info.y_max,
+-- v.pos.x,
+-- v.pos.y,
+-- info.box_count))
+--end
+--\end{codeexample}
+--
+-- As can be seen, the main job of this function is to call a function
+-- on the \TeX\ layer that is called |\pgfgdcallbackrendernode|, which gets
+-- several parameters like the name of the to-be-rendered node or the
+-- (new) position for the node. For almost all methods of the
+-- |Binding| class there is a corresponding ``callback'' macro on the
+-- \TeX\ layer, all of which are implemented in the \pgfname\ library
+-- |graphdrawing|. For details on these callbacks,
+-- please consult the code of that file and of the class
+-- |BindingToPGF| (they are not documented here since they are local
+-- to the binding and should not be called by anyone other than the
+-- binding class).
+
+local BindingToPGF = {
+ storage = Storage.newTableStorage () -- overwrite default storage
+}
+BindingToPGF.__index = BindingToPGF
+setmetatable(BindingToPGF, require "pgf.gd.bindings.Binding") -- subclass of Binding
+
+
+-- Namespace
+require("pgf.gd.bindings").BindingToPGF = BindingToPGF
+
+-- Imports
+local lib = require "pgf.gd.lib"
+
+local Coordinate = require "pgf.gd.model.Coordinate"
+local Path = require "pgf.gd.model.Path"
+
+-- The implementation
+
+-- Forward
+local table_in_pgf_syntax
+local animations_in_pgf_syntax
+local path_in_pgf_syntax
+local coordinate_in_pgf_syntax
+
+
+
+
+-- Scope handling
+
+function BindingToPGF:resumeGraphDrawingCoroutine(text)
+ tex.print(text)
+ tex.print("\\pgfgdresumecoroutinetrue")
+end
+
+
+-- Declarations
+
+function BindingToPGF:declareCallback(t)
+ tex.print("\\pgfgdcallbackdeclareparameter{" .. t.key .. "}{" .. (t.type or "nil") .. "}")
+end
+
+
+
+-- Rendering
+
+function BindingToPGF:renderStart()
+ tex.print("\\pgfgdcallbackbeginshipout")
+end
+
+function BindingToPGF:renderStop()
+ tex.print("\\pgfgdcallbackendshipout")
+end
+
+
+-- Rendering collections
+
+function BindingToPGF:renderCollection(collection)
+ tex.print("\\pgfgdcallbackrendercollection{".. collection.kind .. "}{"
+ .. table_in_pgf_syntax(collection.generated_options) .. "}")
+end
+
+function BindingToPGF:renderCollectionStartKind(kind, layer)
+ tex.print("\\pgfgdcallbackrendercollectionkindstart{" .. kind .. "}{" .. tostring(layer) .. "}")
+end
+
+function BindingToPGF:renderCollectionStopKind(kind, layer)
+ tex.print("\\pgfgdcallbackrendercollectionkindstop{" .. kind .. "}{" .. tostring(layer) .. "}")
+end
+
+-- Printing points
+
+local function to_pt(x)
+ return string.format("%.12fpt", x)
+end
+
+
+-- Managing vertices (pgf nodes)
+
+local boxes = {}
+local box_count = 0
+
+function BindingToPGF:everyVertexCreation(v)
+ local info = self.storage[v]
+
+ -- Save the box!
+ box_count = box_count + 1
+ boxes[box_count] = node.copy_list(tex.box[info.tex_box_number])
+
+ -- Special tex stuff, should not be considered by gd algorithm
+ info.box_count = box_count
+end
+
+function BindingToPGF:renderVertex(v)
+ local info = assert(self.storage[v], "thou shalt not modify the syntactic digraph")
+ tex.print(
+ string.format(
+ "\\pgfgdcallbackrendernode{%s}{%.12fpt}{%.12fpt}{%.12fpt}{%.12fpt}{%.12fpt}{%.12fpt}{%s}{%s}",
+ 'not yet positionedPGFINTERNAL' .. v.name,
+ info.x_min,
+ info.x_max,
+ info.y_min,
+ info.y_max,
+ v.pos.x,
+ v.pos.y,
+ info.box_count,
+ animations_in_pgf_syntax(v.animations)))
+end
+
+function BindingToPGF:retrieveBox(index, box_num)
+ tex.box[box_num] = assert(boxes[index], "no box stored at given index")
+ boxes[index] = nil -- remove from memory
+end
+
+function BindingToPGF:renderVerticesStart()
+ tex.print("\\pgfgdcallbackbeginnodeshipout")
+end
+
+function BindingToPGF:renderVerticesStop()
+ tex.print("\\pgfgdcallbackendnodeshipout")
+end
+
+
+local function rigid(x)
+ if type(x) == "function" then
+ return x()
+ else
+ return x
+ end
+end
+
+
+-- Managing edges
+
+function BindingToPGF:renderEdge(e)
+ local info = assert(self.storage[e], "thou shalt not modify the syntactic digraph")
+
+ local function get_anchor(e, anchor)
+ local a = e.options[anchor]
+ if a and a ~= "" then
+ return "." .. a
+ else
+ return ""
+ end
+ end
+
+ local callback = {
+ '\\pgfgdcallbackedge',
+ '{', e.tail.name .. get_anchor(e, "tail anchor"), '}',
+ '{', e.head.name .. get_anchor(e, "head anchor"), '}',
+ '{', e.direction, '}',
+ '{', info.pgf_options or "", '}',
+ '{', info.pgf_edge_nodes or "", '}',
+ '{', table_in_pgf_syntax(e.generated_options), '}',
+ '{'
+ }
+
+ local i = 1
+ while i <= #e.path do
+ local c = e.path[i]
+ assert (type(c) == "string", "illegal path operand")
+
+ if c == "lineto" then
+ i = i + 1
+ local d = rigid(e.path[i])
+ callback [#callback + 1] = '--(' .. to_pt(d.x) .. ',' .. to_pt(d.y) .. ')'
+ i = i + 1
+ elseif c == "moveto" then
+ i = i + 1
+ local d = rigid(e.path[i])
+ callback [#callback + 1] = '(' .. to_pt(d.x) .. ',' .. to_pt(d.y) .. ')'
+ i = i + 1
+ elseif c == "closepath" then
+ callback [#callback + 1] = '--cycle'
+ i = i + 1
+ elseif c == "curveto" then
+ local d1, d2, d3 = rigid(e.path[i+1]), rigid(e.path[i+2]), rigid(e.path[i+3])
+ i = i + 3
+ callback [#callback + 1] = '..controls(' .. to_pt(d1.x) .. ',' .. to_pt(d1.y) .. ')and('
+ .. to_pt(d2.x) .. ',' .. to_pt(d2.y) .. ')..'
+ callback [#callback + 1] = '(' .. to_pt(d3.x) .. ',' .. to_pt(d3.y) .. ')'
+ i = i + 1
+ else
+ error("illegal operation in edge path")
+ end
+ end
+
+ callback [#callback + 1] = '}'
+ callback [#callback + 1] = '{' .. animations_in_pgf_syntax(e.animations) .. '}'
+
+ -- hand TikZ code over to TeX
+ tex.print(table.concat(callback))
+end
+
+
+function BindingToPGF:renderEdgesStart()
+ tex.print("\\pgfgdcallbackbeginedgeshipout")
+end
+
+function BindingToPGF:renderEdgesStop()
+ tex.print("\\pgfgdcallbackendedgeshipout")
+end
+
+
+-- Vertex creation
+
+function BindingToPGF:createVertex(init)
+ -- Now, go back to TeX...
+ coroutine.yield(
+ table.concat({
+ "\\pgfgdcallbackcreatevertex{", init.name, "}",
+ "{", init.shape, "}",
+ "{", table_in_pgf_syntax(init.generated_options), ",", init.pgf_options or "", "}",
+ "{", (init.text or ""), "}"
+ }))
+ -- ... and come back with a new node!
+end
+
+
+
+-- Local helpers
+
+function table_in_pgf_syntax (t)
+ local prefix = "/graph drawing/"
+ local suffix = "/.try"
+ return table.concat( lib.imap( t, function(table)
+ if table.value then
+ return prefix .. table.key .. suffix .. "={" .. tostring(table.value) .. "}"
+ else
+ return prefix .. table.key .. suffix
+ end
+ end), ",")
+end
+
+
+function animations_in_pgf_syntax (a)
+ return
+ table.concat(
+ lib.imap(
+ a,
+ function(animation)
+ return "\\pgfanimateattribute{" .. animation.attribute .. "}{whom=pgf@gd," ..
+ table.concat(
+ lib.imap (
+ animation.entries,
+ function (entry)
+ return "entry={" .. entry.t .. "s}{" .. to_pgf(entry.value) .. "}"
+ end
+ ), ",") ..
+ "," ..
+ table.concat(
+ lib.imap(
+ animation.options or {},
+ function(table)
+ if table.value then
+ return table.key .. "={" .. to_pgf(table.value) .. "}"
+ else
+ return table.key
+ end
+ end), ",")
+ .. "}"
+ end)
+ )
+end
+
+
+function to_pgf(x)
+ if type (x) == "table" then
+ if getmetatable(x) == Coordinate then
+ return coordinate_in_pgf_syntax(x)
+ elseif getmetatable(x) == Path then
+ return path_in_pgf_syntax(x)
+ else
+ error("illegal table in value of a key to be passed back to pgf")
+ end
+ else
+ return tostring(x)
+ end
+end
+
+function path_in_pgf_syntax (p)
+
+ local s = {}
+
+ local i = 1
+ while i <= #p do
+ local c = p[i]
+ assert (type(c) == "string", "illegal path operand")
+
+ if c == "lineto" then
+ i = i + 1
+ local d = rigid(p[i])
+ s [#s + 1] = '\\pgfpathlineto{\\pgfqpoint{' .. to_pt(d.x) .. '}{' .. to_pt(d.y) .. '}}'
+ i = i + 1
+ elseif c == "moveto" then
+ i = i + 1
+ local d = rigid(p[i])
+ s [#s + 1] = '\\pgfpathmoveto{\\pgfqpoint{' .. to_pt(d.x) .. '}{' .. to_pt(d.y) .. '}}'
+ i = i + 1
+ elseif c == "closepath" then
+ s [#s + 1] = '\\pgfpathclose'
+ i = i + 1
+ elseif c == "curveto" then
+ local d1, d2, d3 = rigid(p[i+1]), rigid(p[i+2]), rigid(p[i+3])
+ i = i + 3
+ s [#s + 1] = '\\pgfpathcurveto{\\pgfqpoint{' .. to_pt(d1.x) .. '}{' .. to_pt(d1.y) .. '}}{\\pgfqpoint{'
+ .. to_pt(d2.x) .. '}{' .. to_pt(d2.y) .. '}}{\\pgfqpoint{'
+ .. to_pt(d3.x) .. '}{' .. to_pt(d3.y) .. '}}'
+ i = i + 1
+ else
+ error("illegal operation in edge path")
+ end
+ end
+
+ return table.concat(s)
+end
+
+function coordinate_in_pgf_syntax(c)
+ return '\\pgfqpoint{'..to_pt(c.x) .. '}{'.. to_pt(c.y) .. '}'
+end
+
+
+return BindingToPGF
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/circular.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/circular.lua
new file mode 100644
index 0000000000..cddfd96dfd
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/circular.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$
+
+
+
+local circular = {}
+
+-- Declare namespace
+require("pgf.gd").circular = circular
+
+
+-- Done
+
+return circular \ No newline at end of file
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/circular/Tantau2012.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/circular/Tantau2012.lua
new file mode 100644
index 0000000000..bebb87ab3e
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/circular/Tantau2012.lua
@@ -0,0 +1,153 @@
+-- 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$
+
+
+-- Imports
+local declare = require("pgf.gd.interface.InterfaceToAlgorithms").declare
+
+local routing = require("pgf.gd.routing")
+
+-- The algorithm class
+local Tantau2012 = {}
+
+---
+declare {
+ key = "simple necklace layout",
+ algorithm = Tantau2012,
+
+ postconditions = {
+ upward_oriented = true
+ },
+
+ documentation_in = "pgf.gd.circular.doc"
+}
+
+
+
+-- Imports
+
+local Coordinate = require "pgf.gd.model.Coordinate"
+local Hints = require "pgf.gd.routing.Hints"
+
+local lib = require "pgf.gd.lib"
+
+
+
+
+-- The implementation
+
+function Tantau2012:run()
+ local g = self.ugraph
+ local vertices = g.vertices
+ local n = #vertices
+
+ local sib_dists = self:computeNodeDistances ()
+ local radii = self:computeNodeRadii()
+ local diam, adjusted_radii = self:adjustNodeRadii(sib_dists, radii)
+
+ -- Compute total necessary length. For this, iterate over all
+ -- consecutive pairs and keep track of the necessary space for
+ -- this node. We imagine the nodes to be aligned from left to
+ -- right in a line.
+ local carry = 0
+ local positions = {}
+ local function wrap(i) return (i-1)%n + 1 end
+ local ideal_pos = 0
+ for i = 1,n do
+ positions[i] = ideal_pos + carry
+ ideal_pos = ideal_pos + sib_dists[i]
+ local node_sep =
+ lib.lookup_option('node post sep', vertices[i], g) +
+ lib.lookup_option('node pre sep', vertices[wrap(i+1)], g)
+ local arc = node_sep + adjusted_radii[i] + adjusted_radii[wrap(i+1)]
+ local needed = carry + arc
+ local dist = math.sin( arc/diam ) * diam
+ needed = needed + math.max ((radii[i] + radii[wrap(i+1)]+node_sep)-dist, 0)
+ carry = math.max(needed-sib_dists[i],0)
+ end
+ local length = ideal_pos + carry
+
+ local radius = length / (2 * math.pi)
+ for i,vertex in ipairs(vertices) do
+ 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
+
+
+function Tantau2012:computeNodeDistances()
+ local sib_dists = {}
+ local sum_length = 0
+ local vertices = self.digraph.vertices
+ for i=1,#vertices do
+ sib_dists[i] = lib.lookup_option('node distance', vertices[i], self.digraph)
+ sum_length = sum_length + sib_dists[i]
+ end
+
+ local missing_length = self.digraph.options['radius'] * 2 * math.pi - sum_length
+ if missing_length > 0 then
+ -- Ok, the sib_dists to not add up to the desired minimum value.
+ -- What should we do? Hmm... We increase all by the missing amount:
+ for i=1,#vertices do
+ sib_dists[i] = sib_dists[i] + missing_length/#vertices
+ end
+ end
+
+ sib_dists.total = math.max(self.digraph.options['radius'] * 2 * math.pi, sum_length)
+
+ return sib_dists
+end
+
+
+function Tantau2012:computeNodeRadii()
+ local radii = {}
+ for i,v in ipairs(self.digraph.vertices) do
+ local min_x, min_y, max_x, max_y = v:boundingBox()
+ local w, h = max_x-min_x, max_y-min_y
+ if v.shape == "circle" or v.shape == "ellipse" then
+ radii[i] = math.max(w,h)/2
+ else
+ radii[i] = math.sqrt(w*w + h*h)/2
+ end
+ end
+ return radii
+end
+
+
+function Tantau2012:adjustNodeRadii(sib_dists,radii)
+ local total = 0
+ local max_rad = 0
+ for i=1,#radii do
+ total = total + 2*radii[i]
+ + lib.lookup_option('node post sep', self.digraph.vertices[i], self.digraph)
+ + lib.lookup_option('node pre sep', self.digraph.vertices[i], self.digraph)
+ max_rad = math.max(max_rad, radii[i])
+ end
+ total = math.max(total, sib_dists.total, max_rad*math.pi)
+ local diam = total/(math.pi)
+
+ -- Now, adjust the radii:
+ local adjusted_radii = {}
+ for i=1,#radii do
+ adjusted_radii[i] = (math.pi - 2*math.acos(radii[i]/diam))*diam/2
+ end
+
+ return diam, adjusted_radii
+end
+
+
+-- done
+
+return Tantau2012
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/circular/doc.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/circular/doc.lua
new file mode 100644
index 0000000000..9198885965
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/circular/doc.lua
@@ -0,0 +1,145 @@
+-- Copyright 2013 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$
+
+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 "simple necklace layout"
+
+summary
+[[
+This simple layout arranges the nodes in a circle, which is
+especially useful for drawing, well, circles of nodes.
+]]
+
+documentation
+[[
+The name |simple necklace layout| is reminiscent of the more general
+``necklace layout'', a term coined by Speckmann and Verbeek in
+their paper
+%
+\begin{itemize}
+ \item
+ Bettina Speckmann and Kevin Verbeek,
+ \newblock Necklace Maps,
+ \newblock \emph{IEEE Transactions on Visualization and Computer
+ Graphics,} 16(6):881--889, 2010.
+\end{itemize}
+
+For a |simple necklace layout|, the centers of the nodes
+are placed on a counter-clockwise circle, starting with the first
+node at the |grow| direction (for |grow'|, the circle is
+clockwise). The order of the nodes is the order in which they appear
+in the graph, the edges are not taken into consideration, unless the
+|componentwise| option is given.
+%
+\begin{codeexample}[
+ preamble={\usetikzlibrary{arrows.meta,graphs,graphdrawing}
+ \usegdlibrary{circular}}]
+\tikz[>={Stealth[round,sep]}]
+ \graph [simple necklace layout, grow'=down, node sep=1em,
+ nodes={draw,circle}, math nodes]
+ {
+ x_1 -> x_2 -> x_3 -> x_4 ->
+ x_5 -> "\dots"[draw=none] -> "x_{n-1}" -> x_n -> x_1
+ };
+\end{codeexample}
+
+When you give the |componentwise| option, the graph will be
+decomposed into connected components, which are then laid out
+individually and packed using the usual component packing
+mechanisms:
+%
+\begin{codeexample}[preamble={\usetikzlibrary{graphs,graphdrawing}
+ \usegdlibrary{circular}}]
+\tikz \graph [simple necklace layout] {
+ a -- b -- c -- d -- a,
+ 1 -- 2 -- 3 -- 1
+};
+\end{codeexample}
+%
+\begin{codeexample}[preamble={\usetikzlibrary{graphs,graphdrawing}
+ \usegdlibrary{circular}}]
+\tikz \graph [simple necklace layout, componentwise] {
+ a -- b -- c -- d -- a,
+ 1 -- 2 -- 3 -- 1
+};
+\end{codeexample}
+
+The nodes are placed in such a way that
+%
+\begin{enumerate}
+ \item The (angular) distance between the centers of consecutive
+ nodes is at least |node distance|,
+ \item the distance between the borders of consecutive nodes is at
+ least |node sep|, and
+ \item the radius is at least |radius|.
+\end{enumerate}
+%
+The radius of the circle is chosen near-minimal such that the above
+properties are satisfied. To be more precise, if all nodes are
+circles, the radius is chosen optimally while for, say, rectangular
+nodes there may be too much space between the nodes in order to
+satisfy the second condition.
+]]
+
+example
+[[
+\tikz \graph [simple necklace layout,
+ node sep=0pt, node distance=0pt,
+ nodes={draw,circle}]
+{ 1 -- 2 [minimum size=30pt] -- 3 --
+ 4 [minimum size=50pt] -- 5 [minimum size=40pt] -- 6 -- 7 };
+]]
+
+example
+[[
+\begin{tikzpicture}[radius=1.25cm]
+ \graph [simple necklace layout,
+ node sep=0pt, node distance=0pt,
+ nodes={draw,circle}]
+ { 1 -- 2 [minimum size=30pt] -- 3 --
+ 4 [minimum size=50pt] -- 5 [minimum size=40pt] -- 6 -- 7 };
+
+ \draw [red] (0,-1.25) circle [];
+\end{tikzpicture}
+]]
+
+example
+[[
+\tikz \graph [simple necklace layout,
+ node sep=0pt, node distance=1cm,
+ nodes={draw,circle}]
+{ 1 -- 2 [minimum size=30pt] -- 3 --
+ 4 [minimum size=50pt] -- 5 [minimum size=40pt] -- 6 -- 7 };
+]]
+
+example
+[[
+\tikz \graph [simple necklace layout,
+ node sep=2pt, node distance=0pt,
+ nodes={draw,circle}]
+{ 1 -- 2 [minimum size=30pt] -- 3 --
+ 4 [minimum size=50pt] -- 5 [minimum size=40pt] -- 6 -- 7 };
+]]
+
+example
+[[
+\tikz \graph [simple necklace layout,
+ node sep=0pt, node distance=0pt,
+ nodes={rectangle,draw}]
+{ 1 -- 2 [minimum size=30pt] -- 3 --
+ 4 [minimum size=50pt] -- 5 [minimum size=40pt] -- 6 -- 7 };
+]]
+--------------------------------------------------------------------
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/circular/library.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/circular/library.lua
new file mode 100644
index 0000000000..016cfe483f
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/circular/library.lua
@@ -0,0 +1,30 @@
+-- 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$
+
+
+-- Imports
+local declare = require "pgf.gd.interface.InterfaceToAlgorithms".declare
+
+---
+-- ``Circular'' graph drawing algorithms arrange the nodes of a graph
+-- on one of more circles.
+--
+-- @library
+
+local circular -- Library name
+
+-- Load declarations from:
+
+-- Load algorithms from:
+require "pgf.gd.circular.Tantau2012"
+
+
+-- General declarations
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/control.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/control.lua
new file mode 100644
index 0000000000..a3bec1fef3
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/control.lua
@@ -0,0 +1,26 @@
+-- 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$
+
+
+
+-- Imports
+
+require "pgf"
+require "pgf.gd"
+
+
+-- Declare namespace
+pgf.gd.control = {}
+
+
+-- Done
+
+return pgf.gd.control \ No newline at end of file
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/control/Anchoring.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/control/Anchoring.lua
new file mode 100644
index 0000000000..03a05ee627
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/control/Anchoring.lua
@@ -0,0 +1,104 @@
+-- Copyright 2012 by Till Tantau
+--
+-- This file may be distributed an/or modified
+--
+-- 1. under the LaTeX Project Public License and/or
+-- 2. under the GNU Public License
+--
+-- See the file doc/generic/pgf/licenses/LICENSE for more information
+
+-- @release $Header$
+
+
+
+---
+-- @section subsection {Anchoring a Graph}
+--
+-- \label{subsection-library-graphdrawing-anchoring}
+--
+-- A graph drawing algorithm must compute positions of the nodes of a
+-- graph, but the computed positions are only \emph{relative} (``this
+-- node is left of this node, but above that other node''). It is not
+-- immediately obvious where the ``the whole graph'' should be placed
+-- \emph{absolutely} once all relative positions have been computed. In
+-- case that the graph consists of several unconnected components, the
+-- situation is even more complicated.
+--
+-- The order in which the algorithm layer determines the node at which
+-- the graph should be anchored:
+-- %
+-- \begin{enumerate}
+-- \item If the |anchor node=|\meta{node name} option given to the graph
+-- as a whole, the graph is anchored at \meta{node name}, provided
+-- there is a node of this name in the graph. (If there is no node of
+-- this name or if it is misspelled, the effect is the same as if this
+-- option had not been given at all.)
+-- \item Otherwise, if there is a node where the |anchor here| option is
+-- specified, the first node with this option set is used.
+-- \item Otherwise, if there is a node where the |desired at| option is
+-- set (perhaps implicitly through keys like |x|), the first such node
+-- is used.
+-- \item Finally, in all other cases, the first node is used.
+-- \end{enumerate}
+--
+-- In the above description, the ``first'' node refers to the node first
+-- encountered in the specification of the graph.
+--
+-- Once the node has been determined, the graph is shifted so that
+-- this node lies at the position specified by |anchor at|.
+--
+-- @end
+
+
+
+local Anchoring = {}
+
+
+-- Namespace
+require("pgf.gd.control").Anchoring = Anchoring
+
+
+-- Imports
+local Coordinate = require("pgf.gd.model.Coordinate")
+local declare = require "pgf.gd.interface.InterfaceToAlgorithms".declare
+
+
+
+---
+declare {
+ key = "desired at",
+ type = "coordinate",
+ documentation_in = "pgf.gd.control.doc"
+}
+
+---
+declare {
+ key = "anchor node",
+ type = "string",
+ documentation_in = "pgf.gd.control.doc"
+}
+
+
+---
+declare {
+ key = "anchor at",
+ type = "canvas coordinate",
+ initial = "(0pt,0pt)",
+ documentation_in = "pgf.gd.control.doc"
+}
+
+
+---
+declare {
+ key = "anchor here",
+ type = "boolean",
+ documentation_in = "pgf.gd.control.doc"
+}
+
+
+
+
+
+-- Done
+
+return Anchoring \ No newline at end of file
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/control/ComponentAlign.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/control/ComponentAlign.lua
new file mode 100644
index 0000000000..72a82be978
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/control/ComponentAlign.lua
@@ -0,0 +1,540 @@
+-- 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$
+
+
+local declare = require "pgf.gd.interface.InterfaceToAlgorithms".declare
+
+
+---
+-- @section subsubsection {Aligning Components}
+--
+-- When components are placed next to each from left to right, it
+-- is not immediately clear how the components should be aligned
+-- vertically. What happens is that in each component a horizontal line is
+-- determined and then all components are shifted vertically so that the
+-- lines are aligned. There are different strategies for choosing these
+-- ``lines'', see the description of the options described later on.
+-- When the |component direction| option is used to change the direction
+-- in which components are placed, it certainly make no longer sense to
+-- talk about ``horizontal'' and ``vertical'' lines. Instead, what
+-- actually happens is that the alignment does not consider
+-- ``horizontal'' lines, but lines that go in the direction specified by
+-- |component direction| and aligns them by moving components along a
+-- line that is perpendicular to the line. For these reasons, let us call
+-- the line in the component direction the \emph{alignment line} and a
+-- line that is perpendicular to it the \emph{shift line}.
+--
+-- The first way of specifying through which point of a component the
+-- alignment line should get is to use the option |align here|.
+-- In many cases, however, you will not wish to specify an alignment node
+-- manually in each component. Instead, you will use the
+-- |component align| key to specify a \emph{strategy} that should be used to
+-- automatically determine such a node.
+--
+-- Using a combination of |component direction| and |component align|,
+-- numerous different packing strategies can be configured. However,
+-- since names like |counterclockwise| are a bit hard to remember and to
+-- apply in practice, a number of easier-to-remember keys are predefined
+-- that combine an alignment and a direction.
+--
+-- @end
+
+---
+
+declare {
+ key = "align here",
+ type = "boolean",
+
+ summary = [["
+ When this option is given to a node, this alignment line will go
+ through the origin of this node. If this option is passed to more
+ than one node of a component, the node encountered first in the
+ component is used.
+ "]],
+ examples = [["
+ \tikz \graph [binary tree layout, nodes={draw}]
+ { a, b -- c[align here], d -- e[second, align here] -- f };
+ "]]
+}
+
+---
+
+declare {
+ key = "component align",
+ type = "string",
+ initial = "first node",
+
+ summary = [["
+ Specifies a ``strategy'' for the alignment of components.
+ "]],
+ documentation = [["
+ The following values are permissible:
+ %
+ \begin{itemize}
+ \item \declare{|first node|}
+
+ In each component, the alignment line goes through the center of
+ the first node of the component encountered during specification
+ of the component.
+ %
+\begin{codeexample}[preamble={\usetikzlibrary{graphs,graphdrawing}
+ \usegdlibrary{trees}}]
+\tikz \graph [binary tree layout, nodes={draw},
+ component align=first node]
+ { a, b -- c, d -- e[second] -- f };
+\end{codeexample}
+ %
+ \item \declare{|center|}
+
+ The nodes of the component are projected onto the shift line. The
+ alignment line is now chosen so that it is exactly in the middle
+ between the maximum and minimum value that the projected nodes
+ have on the shift line.
+ %
+\begin{codeexample}[preamble={\usetikzlibrary{graphs,graphdrawing}
+ \usegdlibrary{trees}}]
+\tikz \graph [binary tree layout, nodes={draw},
+ component align=center]
+ { a, b -- c, d -- e[second] -- f };
+\end{codeexample}
+ %
+\begin{codeexample}[preamble={\usetikzlibrary{graphs,graphdrawing}
+ \usegdlibrary{trees}}]
+\tikz \graph [binary tree layout, nodes={draw},
+ component direction=90,
+ component align=center]
+ { a, b -- c, d -- e[second] -- f };
+\end{codeexample}
+ %
+ \item \declare{|counterclockwise|}
+
+ As for |center|, we project the nodes of the component onto the
+ shift line. The alignment line is now chosen so that it goes
+ through the center of the node whose center has the highest
+ projected value.
+ %
+\begin{codeexample}[preamble={\usetikzlibrary{graphs,graphdrawing}
+ \usegdlibrary{trees}}]
+\tikz \graph [binary tree layout, nodes={draw},
+ component align=counterclockwise]
+ { a, b -- c, d -- e[second] -- f };
+\end{codeexample}
+ %
+\begin{codeexample}[preamble={\usetikzlibrary{graphs,graphdrawing}
+ \usegdlibrary{trees}}]
+\tikz \graph [binary tree layout, nodes={draw},
+ component direction=90,
+ component align=counterclockwise]
+ { a, b -- c, d -- e[second] -- f };
+\end{codeexample}
+ The name |counterclockwise| is intended to indicate that the align
+ line goes through the node that comes last if we go from the
+ alignment direction in a counter-clockwise direction.
+ \item \declare{|clockwise|}
+
+ Works like |counterclockwise|, only in the other direction:
+ %
+\begin{codeexample}[preamble={\usetikzlibrary{graphs,graphdrawing}
+ \usegdlibrary{trees}}]
+\tikz \graph [binary tree layout, nodes={draw},
+ component align=clockwise]
+ { a, b -- c, d -- e[second] -- f };
+\end{codeexample}
+ %
+\begin{codeexample}[preamble={\usetikzlibrary{graphs,graphdrawing}
+ \usegdlibrary{trees}}]
+\tikz \graph [binary tree layout, nodes={draw},
+ component direction=90,
+ component align=clockwise]
+ { a, b -- c, d -- e[second] -- f };
+\end{codeexample}
+ %
+ \item \declare{|counterclockwise bounding box|}
+
+ This method is quite similar to |counterclockwise|, only the
+ alignment line does not go through the center of the node with a
+ maximum projected value on the shift line, but through the maximum
+ value of the projected bounding boxes. For a left-to-right
+ packing, this means that the components are aligned so that the
+ bounding boxes of the components are aligned at the top.
+ %
+\begin{codeexample}[preamble={\usetikzlibrary{graphs,graphdrawing}
+ \usegdlibrary{trees}}]
+\tikz \graph [tree layout, nodes={draw, align=center},
+ component sep=0pt,
+ component align=counterclockwise]
+ { a, "high\\node" -- b};\quad
+\tikz \graph [tree layout, nodes={draw, align=center},
+ component sep=0pt,
+ component align=counterclockwise bounding box]
+ { a, "high\\node" -- b};
+\end{codeexample}
+ %
+ \item \declare{|clockwise bounding box|}
+
+ Works like |counterclockwise bounding box|.
+ \end{itemize}
+ "]]
+}
+
+---
+
+declare {
+ key = "components go right top aligned",
+ use = {
+ { key = "component direction", value = 0},
+ { key = "component align", value = "counterclockwise"},
+ },
+
+ summary = [["
+ Shorthand for |component direction=right| and
+ |component align=counterclockwise|. This means that, as the name
+ suggest, the components will be placed left-to-right and they are
+ aligned such that their top nodes are in a line.
+ "]],
+ examples = [["
+ \tikz \graph [tree layout, nodes={draw, align=center},
+ components go right top aligned]
+ { a, "high\\node" -- b};
+ "]]
+}
+
+---
+
+declare {
+ key = "components go right absolute top aligned",
+ use = {
+ { key = "component direction", value=0},
+ { key = "component align", value = "counterclockwise bounding box"},
+ },
+
+ summary = [["
+ Like |components go right top aligned|, but with
+ |component align| set to |counterclockwise| |bounding box|.
+ This means that the components will be aligned with their bounding
+ boxed being top-aligned.
+ "]],
+ examples = [["
+ \tikz \graph [tree layout, nodes={draw, align=center},
+ components go right absolute top aligned]
+ { a, "high\\node" -- b};
+ "]]
+}
+
+---
+
+declare {
+ key = "components go right bottom aligned",
+ use = {
+ { key = "component direction", value=0},
+ { key = "component align", value = "clockwise"},
+ },
+
+ summary = "See the other |components go ...| keys."
+}
+
+---
+--
+
+declare {
+ key = "components go right absolute bottom aligned",
+ use = {
+ { key = "component direction", value=0},
+ { key = "component align", value = "clockwise bounding box"},
+ },
+
+ summary = "See the other |components go ...| keys."
+}
+
+
+---
+
+declare {
+ key = "components go right center aligned",
+ use = {
+ { key = "component direction", value=0},
+ { key = "component align", value = "center"},
+ },
+
+ summary = "See the other |components go ...| keys."
+}
+
+
+---
+
+declare {
+ key = "components go right",
+ use = {
+ { key = "component direction", value=0},
+ { key = "component align", value = "first node"},
+ },
+
+ summary = [["
+ Shorthand for |component direction=right| and
+ |component align=first node|.
+ "]]
+ }
+
+
+---
+
+declare {
+ key = "components go left top aligned",
+ use = {
+ { key = "component direction", value=180},
+ { key = "component align", value = "clockwise"},
+ },
+
+ summary = "See the other |components go ...| keys.",
+
+ examples = [["
+ \tikz \graph [tree layout, nodes={draw, align=center},
+ components go left top aligned]
+ { a, "high\\node" -- b};
+ "]]
+}
+
+---
+--
+
+declare {
+ key = "components go left absolute top aligned",
+ use = {
+ { key = "component direction", value=180},
+ { key = "component align", value = "clockwise bounding box"},
+ },
+
+ summary = "See the other |components go ...| keys."
+}
+
+
+---
+--
+
+declare {
+ key = "components go left bottom aligned",
+ use = {
+ { key = "component direction", value=180},
+ { key = "component align", value = "counterclockwise"},
+ },
+
+ summary = "See the other |components go ...| keys."
+}
+
+
+---
+--
+
+declare {
+ key = "components go left absolute bottom aligned",
+ use = {
+ { key = "component direction", value=180},
+ { key = "component align", value = "counterclockwise bounding box"},
+ },
+
+ summary = "See the other |components go ...| keys."
+}
+
+
+---
+--
+
+declare {
+ key = "components go left center aligned",
+ use = {
+ { key = "component direction", value=180},
+ { key = "component align", value = "center"},
+ },
+ summary = "See the other |components go ...| keys."
+}
+
+
+---
+--
+
+declare {
+ key = "components go left",
+ use = {
+ { key = "component direction", value=180},
+ { key = "component align", value = "first node"},
+ },
+ summary = "See the other |components go ...| keys."
+}
+
+
+
+---
+
+declare {
+ key = "components go down right aligned",
+ use = {
+ { key = "component direction", value=270},
+ { key = "component align", value = "counterclockwise"},
+ },
+ summary = "See the other |components go ...| keys.",
+
+ examples = {[["
+ \tikz \graph [tree layout, nodes={draw, align=center},
+ components go down left aligned]
+ { a, hello -- {world,s} };
+ "]],[["
+ \tikz \graph [tree layout, nodes={draw, align=center},
+ components go up absolute left aligned]
+ { a, hello -- {world,s}};
+ "]]
+ }
+}
+
+---
+--
+
+declare {
+ key = "components go down absolute right aligned",
+ use = {
+ { key = "component direction", value=270},
+ { key = "component align", value = "counterclockwise bounding box"},
+ },
+ summary = "See the other |components go ...| keys."
+}
+
+
+---
+--
+
+declare {
+ key = "components go down left aligned",
+ use = {
+ { key = "component direction", value=270},
+ { key = "component align", value = "clockwise"},
+ },
+ summary = "See the other |components go ...| keys."
+}
+
+
+---
+--
+
+declare {
+ key = "components go down absolute left aligned",
+ use = {
+ { key = "component direction", value=270},
+ { key = "component align", value = "clockwise bounding box"},
+ },
+ summary = "See the other |components go ...| keys."
+}
+
+
+---
+--
+
+declare {
+ key = "components go down center aligned",
+ use = {
+ { key = "component direction", value=270},
+ { key = "component align", value = "center"},
+ },
+ summary = "See the other |components go ...| keys."
+}
+
+
+---
+--
+
+declare {
+ key = "components go down",
+ use = {
+ { key = "component direction", value=270},
+ { key = "component align", value = "first node"},
+ },
+ summary = "See the other |components go ...| keys."
+}
+
+---
+--
+
+declare {
+ key = "components go up right aligned",
+ use = {
+ { key = "component direction", value=90},
+ { key = "component align", value = "clockwise"},
+ },
+ summary = "See the other |components go ...| keys."
+}
+
+
+---
+--
+
+declare {
+ key = "components go up absolute right aligned",
+ use = {
+ { key = "component direction", value=90},
+ { key = "component align", value = "clockwise bounding box"},
+ },
+ summary = "See the other |components go ...| keys."
+}
+
+
+---
+--
+
+declare {
+ key = "components go up left aligned",
+ use = {
+ { key = "component direction", value=90},
+ { key = "component align", value = "counterclockwise"},
+ },
+ summary = "See the other |components go ...| keys."
+}
+
+
+---
+--
+
+declare {
+ key = "components go up absolute left aligned",
+ use = {
+ { key = "component direction", value=90},
+ { key = "component align", value = "counterclockwise bounding box"},
+ },
+ summary = "See the other |components go ...| keys."
+}
+
+
+---
+--
+
+declare {
+ key = "components go up center aligned",
+ use = {
+ { key = "component direction", value=90},
+ { key = "component align", value = "center"},
+ },
+ summary = "See the other |components go ...| keys."
+}
+
+
+---
+--
+
+declare {
+ key = "components go up",
+ use = {
+ { key = "component direction", value=90},
+ { key = "component align", value = "first node"},
+ },
+ summary = "See the other |components go ...| keys."
+}
+
+
+
+
+return Components
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/control/ComponentDirection.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/control/ComponentDirection.lua
new file mode 100644
index 0000000000..f941ff226e
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/control/ComponentDirection.lua
@@ -0,0 +1,58 @@
+-- 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$
+
+
+local declare = require "pgf.gd.interface.InterfaceToAlgorithms".declare
+
+
+---
+-- @section subsubsection {Arranging Components in a Certain Direction}
+--
+-- @end
+
+---
+
+declare {
+ key = "component direction",
+ type = "direction",
+ initial = "0",
+
+ summary = [["
+ The \meta{angle} is used to determine the relative position of each
+ component relative to the previous one. The direction need not be a
+ multiple of |90|. As usual, you can use texts like |up| or
+ |right| instead of a number.
+ "]],
+ documentation = [["
+ As the examples show, the direction only has an influence on the
+ relative positions of the components, not on the direction of growth
+ inside the components. In particular, the components are not rotated
+ by this option in any way. You can use the |grow| option or |orient|
+ options to orient individual components.
+ "]],
+ examples = {[["
+ \tikz \graph [tree layout, nodes={inner sep=1pt,draw,circle},
+ component direction=left]
+ { a, b, c -- d -- e, f -- g };
+ "]],[["
+ \tikz \graph [tree layout, nodes={inner sep=1pt,draw,circle},
+ component direction=10]
+ { a, b, c -- d -- e, f -- g };
+ "]],[["
+ \tikz \graph [tree layout, nodes={inner sep=1pt,draw,circle},
+ component direction=up]
+ { a, b, c [grow=right] -- d -- e, f[grow=45] -- g };
+ "]]
+ }
+}
+
+
+return Components \ No newline at end of file
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/control/ComponentDistance.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/control/ComponentDistance.lua
new file mode 100644
index 0000000000..a2d04a178f
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/control/ComponentDistance.lua
@@ -0,0 +1,108 @@
+-- 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$
+
+
+local declare = require "pgf.gd.interface.InterfaceToAlgorithms".declare
+
+
+---
+-- @section subsubsection {The Distance Between Components}
+--
+-- Once the components of a graph have been oriented, sorted, aligned,
+-- and a direction has been chosen, it remains to determine the distance
+-- between adjacent components. Two methods are available for computing
+-- this distance, as specified by the following option:
+--
+-- @end
+
+---
+
+declare {
+ key = "component packing",
+ type = "string",
+ initial = "skyline",
+
+ documentation = [["
+ Given two components, their distance is computed as follows in
+ dependence of \meta{method}:
+ %
+ \begin{itemize}
+ \item \declare{|rectangular|}
+
+ Imagine a bounding box to be drawn around both components. They
+ are then shifted such that the padding (separating distance)
+ between the two boxes is the current value of |component sep|.
+ %
+ \begin{codeexample}[preamble={\usetikzlibrary{graphs,graphdrawing}
+ \usegdlibrary{trees}}]
+ \tikz \graph [tree layout, nodes={draw}, component sep=0pt,
+ component packing=rectangular]
+ { a -- long text, longer text -- b};
+ \end{codeexample}
+ %
+ \item \declare{|skyline|}
+
+ The ``skyline method'' is used to compute the distance. It works
+ as follows: For simplicity, assume that the component direction is
+ right (other case work similarly, only everything is
+ rotated). Imaging the second component to be placed far right
+ beyond the first component. Now start moving the second component
+ back to the left until one of the nodes of the second component
+ touches a node of the first component, and stop. Again, the
+ padding |component sep| can be used to avoid the nodes actually
+ touching each other.
+ %
+ \begin{codeexample}[preamble={\usetikzlibrary{graphs,graphdrawing}
+ \usegdlibrary{trees}}]
+ \tikz \graph [tree layout, nodes={draw}, component sep=0pt,
+ level distance=1.5cm,
+ component packing=skyline]
+ { a -- long text, longer text -- b};
+ \end{codeexample}
+
+ In order to avoid nodes of the second component ``passing through
+ a hole in the first component'', the actual algorithm is a bit
+ more complicated: For both components, a ``skyline'' is
+ computed. For the first component, consider an arbitrary
+ horizontal line. If there are one or more nodes on this line, the
+ rightmost point on any of the bounding boxes of these nodes will
+ be the point on the skyline of the first component for this
+ line. Similarly, for the second component, for each horizontal
+ level the skyline is given by the leftmost point on any of the
+ bounding boxes intersecting the line.
+
+ Now, the interesting case are horizontal lines that do not
+ intersect any of the nodes of the first and/or second
+ component. Such lines represent ``holes'' in the skyline. For
+ them, the following rule is used: Move the horizontal line upward
+ and downward as little as possible until a height is reached where
+ there is a skyline defined. Then the skyline position on the
+ original horizontal line is the skyline position at the reached
+ line, minus (or, for the second component, plus) the distance by
+ which the line was moved. This means that the holes are ``filled
+ up by slanted roofs''.
+ %
+ \begin{codeexample}[preamble={\usetikzlibrary{graphs,graphdrawing}
+ \usegdlibrary{trees}}]
+ \begin{tikzpicture}
+ \graph [tree layout, nodes={draw}, component sep=0pt,
+ component packing=skyline]
+ { a -- long text, longer text -- b};
+ \draw[red] (long text.north east) -- ++(north west:1cm);
+ \end{tikzpicture}
+ \end{codeexample}
+
+ \end{itemize}
+ "]]
+}
+
+
+return Components
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/control/ComponentOrder.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/control/ComponentOrder.lua
new file mode 100644
index 0000000000..78537b614c
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/control/ComponentOrder.lua
@@ -0,0 +1,105 @@
+-- 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$
+
+
+local declare = require "pgf.gd.interface.InterfaceToAlgorithms".declare
+
+
+
+
+---
+-- @section subsubsection {Ordering the Components}
+--
+-- The different connected components of the graph are collected in a
+-- list. The ordering of the nodes in this list can be configured using
+-- the following key.
+--
+-- @end
+
+
+---
+
+declare {
+ key = "component order",
+ type = "string",
+ initial = "by first specified node",
+
+ summary = [["
+ Selects a ``strategy'' for ordering the components. By default,
+ they are ordered in the way they appear in the input.
+ "]],
+ documentation = [["
+ The following values are permissible for \meta{strategy}
+ %
+ \begin{itemize}
+ \item \declare{|by first specified node|}
+
+ The components are ordered ``in the way they appear in the input
+ specification of the graph''. More precisely, for each component
+ consider the node that is first encountered in the description
+ of the graph. Order the components in the same way as these nodes
+ appear in the graph description.
+ \item \declare{|increasing node number|}
+
+ The components are ordered by increasing number of nodes. For
+ components with the same number of nodes, the first node in each
+ component is considered and they are ordered according to the
+ sequence in which these nodes appear in the input.
+
+ \item \declare{|decreasing node number|}
+ As above, but in decreasing order.
+ \end{itemize}
+ "]],
+ examples = {[["
+ \tikz \graph [tree layout, nodes={inner sep=1pt,draw,circle},
+ component order=by first specified node]
+ { a, b, c, f -- g, c -- d -- e };
+ "]],[["
+ \tikz \graph [tree layout, nodes={inner sep=1pt,draw,circle},
+ component order=increasing node number]
+ { a, b, c -- d -- e, f -- g };
+ "]]
+ }
+}
+
+
+---
+
+declare {
+ key = "small components first",
+ use = {
+ { key = "component order", value = "increasing node number" }
+ },
+
+ summary = [["
+ A shorthand for |component order=increasing node number|.
+ "]]
+ }
+
+---
+
+declare {
+ key = "large components first",
+ use = {
+ { key = "component order", value = "decreasing node number" },
+ },
+ summary = [["
+ A shorthand for |component order=decreasing node number|.
+ "]],
+ examples = [["
+ \tikz \graph [tree layout, nodes={inner sep=1pt,draw,circle},
+ large components first]
+ { a, b, c -- d -- e, f -- g };
+ "]]
+}
+
+
+return Components \ No newline at end of file
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/control/Components.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/control/Components.lua
new file mode 100644
index 0000000000..c5019a56e2
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/control/Components.lua
@@ -0,0 +1,127 @@
+-- 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$
+
+
+-- Imports
+
+local declare = require "pgf.gd.interface.InterfaceToAlgorithms".declare
+
+
+---
+-- @section subsection {Packing of Connected Components}
+--
+-- \label{subsection-gd-component-packing}
+-- \label{section-gd-packing}
+--
+-- Graphs may be composed of subgraphs or \emph{components} that are not
+-- connected to each other. In order to draw these nicely, most graph
+-- drawing algorithms split them into separate graphs, compute
+-- their layouts with the same graph drawing algorithm independently and,
+-- in a postprocessing step, arrange them next to each other. Note,
+-- however, that some graph drawing algorithms can also arrange the nodes
+-- of the graph in a uniform way even for unconnected components (the
+-- |simple necklace layout| is a case in point); for such algorithms you can
+-- choose whether they should be applied to each component individually
+-- or not (if not, the following options do not apply). To configure
+-- which is the case, use the |componentwise| key.
+--
+-- The default method for placing the different components works as
+-- follows:
+-- %
+-- \begin{enumerate}
+-- \item For each component, a layout is determined and the component is
+-- oriented as described
+-- Section~\ref{subsection-library-graphdrawing-standard-orientation}
+-- on the orientation of graphs.
+-- \item The components are sorted as prescribed by the
+-- |component order| key.
+-- \item The first component is now placed (conceptually) at the
+-- origin. (The final position of this and all other components will be
+-- determined later, namely in the anchoring phase, but let us imagine
+-- that the first component lies at the origin at this point.)
+-- \item The second component is now positioned relative to the first
+-- component. The ``direction'' in which the next component is placed
+-- relative to the first one is determined by the |component direction|
+-- key, so components can be placed from left to right or up to down or
+-- in any other direction (even something like $30^\circ$). However,
+-- both internally and in the following description, we assume that the
+-- components are placed from left to right; other directions are
+-- achieved by doing some (clever) rotating of the arrangement achieved
+-- in this way.
+--
+-- So, we now wish to place the second component to the right of the
+-- first component. The component is first shifted vertically according
+-- to some alignment strategy. For instance, it can be shifted so that
+-- the topmost node of the first component and the topmost node of the
+-- second component have the same vertical position. Alternatively, we
+-- might require that certain ``alignment nodes'' in both components
+-- have the same vertical position. There are several other strategies,
+-- which can be configured using the |component align| key.
+--
+-- One the vertical position has been fixed, the horizontal position is
+-- computed. Here, two different strategies are available: First, image
+-- rectangular bounding boxed to be drawn around both components. Then
+-- we shift the second component such that the right border of the
+-- bounding box of the first component touches the left border of the
+-- bounding box of the second component. Instead of having the bounding
+-- boxes ``touch'', we can also have a padding of |component sep|
+-- between them. The second strategy is more involved and also known as
+-- a ``skyline'' strategy, where (roughly) the components are
+-- ``moved together as near as possible so that nodes do not touch''.
+-- \item
+-- After the second component has been placed, the third component is
+-- considered and positioned relative to the second one, and so on.
+-- \item
+-- At the end, as hinted at earlier, the whole arrangement is rotate so
+-- that instead of ``going right'' the component go in the direction of
+-- |component direction|. Note, however, that this rotation applies only
+-- to the ``shift'' of the components; the components themselves are
+-- not rotated. Fortunately, this whole rotation process happens in the
+-- background and the result is normally exactly what you would expect.
+-- \end{enumerate}
+--
+-- In the following, we go over the different keys that can be used to
+-- configure the component packing.
+--
+-- @end
+
+
+---
+
+declare {
+ key = "componentwise",
+ type = "boolean",
+
+ summary = [["
+ For algorithms that also support drawing unconnected graphs, use
+ this key to enforce that the components of the graph are,
+ nevertheless, laid out individually. For algorithms that do not
+ support laying out unconnected graphs, this option has no effect;
+ rather it works as if this option were always set.
+ "]],
+ examples = {[["
+ \tikz \graph [simple necklace layout]
+ {
+ a -- b -- c -- d -- a,
+ 1 -- 2 -- 3 -- 1
+ };
+ "]],[[",
+ \tikz \graph [simple necklace layout, componentwise]
+ {
+ a -- b -- c -- d -- a,
+ 1 -- 2 -- 3 -- 1
+ };
+ "]]
+ }
+}
+
+
+
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/control/Distances.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/control/Distances.lua
new file mode 100644
index 0000000000..6dc5a13018
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/control/Distances.lua
@@ -0,0 +1,454 @@
+-- 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$
+
+
+local declare = require "pgf.gd.interface.InterfaceToAlgorithms".declare
+local lib = require "pgf.gd.lib"
+
+---
+-- @section subsection {Padding and Node Distances}
+--
+-- \label{subsection-gd-dist-pad}
+--
+-- In many drawings, you may wish to specify how ``near'' two nodes should
+-- be placed by a graph drawing algorithm. Naturally, this depends
+-- strongly on the specifics of the algorithm, but there are a number of
+-- general keys that will be used by many algorithms.
+--
+-- There are different kinds of objects for which you can specify
+-- distances and paddings:
+-- %
+-- \begin{itemize}
+-- \item You specify the ``natural'' distance between nodes
+-- connected by an edge using |node distance|, which is also available in
+-- normal \tikzname\ albeit for a slightly different purpose. However,
+-- not every algorithm will (or can) honor the key; see the description
+-- of each algorithm what it will ``make of this option''.
+-- \item A number of graph drawing algorithms arrange nodes in layers
+-- (or levels); we refer
+-- to the nodes on the same layer as siblings (although, in a tree,
+-- siblings are only nodes with the same parent; nevertheless we use
+-- ``sibling'' loosely also for nodes that are more like ``cousins'').
+-- \item When a graph consists of several connected component, many graph
+-- drawing algorithms will layout these components individually. The
+-- different components will then be arranged next to each other, see
+-- Section~\ref{section-gd-packing} for the details, such that between
+-- the nodes of any two components a padding is available.
+-- \end{itemize}
+--
+-- @end
+
+
+
+
+---
+
+declare {
+ key = "node distance",
+ type = "length",
+ initial = "1cm",
+
+ summary = [["
+ This is minimum distance that the centers of nodes connected by an
+ edge should have. It will not always be possible to satisfy this
+ desired distance, for instance in case the nodes are too big. In
+ this case, the \meta{length} is just considered as a lower bound.
+ "]],
+ examples = [["
+ \begin{tikzpicture}
+ \graph [simple necklace layout, node distance=1cm, node sep=0pt,
+ nodes={draw,circle,as=.}]
+ {
+ 1 -- 2 [minimum size=2cm] -- 3 --
+ 4 -- 5 -- 6 -- 7 --[orient=up] 8
+ };
+ \draw [red,|-|] (1.center) -- ++(0:1cm);
+ \draw [red,|-|] (5.center) -- ++(180:1cm);
+ \end{tikzpicture}
+ "]]
+}
+
+
+---
+
+declare {
+ key = "node pre sep",
+ type = "length",
+ initial = ".333em",
+
+ summary = [["
+ This is a minimum ``padding'' or ``separation'' between the border
+ of nodes connected by an edge. Thus, if nodes are so big that nodes
+ with a distance of |node distance| would overlap (or
+ just come with \meta{dimension} distance of one another), their
+ distance is enlarged so that this distance is still satisfied.
+ The |pre| means that the padding is added to the node ``at the
+ front''. This make sense only for some algorithms, like for a
+ simple necklace layout.
+ "]],
+ examples = {[["
+ \tikz \graph [simple necklace layout, node distance=0cm, nodes={circle,draw}]
+ { 1--2--3--4--5--1 };
+ "]],[["
+ \tikz \graph [simple necklace layout, node distance=0cm, node sep=0mm,
+ nodes={circle,draw}]
+ { 1--2--3[node pre sep=5mm]--4--5[node pre sep=1mm]--1 };
+ "]]
+ }
+}
+
+---
+
+declare {
+ key = "node post sep",
+ type = "length",
+ initial = ".333em",
+
+ summary = [["
+ Works like |node pre sep|.
+ "]]
+}
+
+
+
+---
+-- @param length A length.
+
+declare {
+ key = "node sep",
+ type = "length",
+ use = {
+ { key = "node pre sep", value = function(v) return v/2 end },
+ { key = "node post sep", value = function(v) return v/2 end },
+ },
+ summary = [["
+ A shorthand for setting both |node pre sep| and |node post sep| to
+ $\meta{length}/2$.
+ "]]
+}
+
+
+---
+
+declare {
+ key = "level distance",
+ type = "length",
+ initial = "1cm",
+
+ summary = [["
+ This is minimum distance that the centers of nodes on one
+ level should have from the centers of nodes on the next level. It
+ will not always be possible to satisfy this desired distance, for
+ instance in case the nodes are too big. In this case, the
+ \meta{length} is just considered as a lower bound.
+ "]],
+ examples = [["
+ \begin{tikzpicture}[inner sep=2pt]
+ \draw [help lines] (0,0) grid (3.5,2);
+ \graph [layered layout, level distance=1cm, level sep=0]
+ { 1 [x=1,y=2] -- 2 -- 3 -- 1 };
+ \graph [layered layout, level distance=5mm, level sep=0]
+ { 1 [x=3,y=2] -- 2 -- 3 -- 1, 3 -- {4,5} -- 6 -- 3 };
+ \end{tikzpicture}
+ "]]
+}
+
+---
+declare {
+ key = "layer distance",
+ type = "length",
+ use = {
+ { key = "level distance", value = lib.id },
+ },
+ summary = "An alias for |level distance|"
+}
+
+---
+declare {
+ key = "level pre sep",
+ type = "length",
+ initial = ".333em",
+
+ summary = [["
+ This is a minimum ``padding'' or ``separation'' between the border
+ of the nodes on a level to any nodes on the previous level. Thus, if
+ nodes are so big that nodes on consecutive levels would overlap (or
+ just come with \meta{length} distance of one another), their
+ distance is enlarged so that this distance is still satisfied.
+ If a node on the previous level also has a |level post sep|, this
+ post padding and the \meta{dimension} add up. Thus, these keys
+ behave like the ``padding'' keys rather
+ than the ``margin'' key of cascading style sheets.
+ "]],
+ examples = [["
+ \begin{tikzpicture}[inner sep=2pt, level sep=0pt, sibling distance=0pt]
+ \draw [help lines] (0,0) grid (3.5,2);
+ \graph [layered layout, level distance=0cm, nodes=draw]
+ { 1 [x=1,y=2] -- {2,3[level pre sep=1mm],4[level pre sep=5mm]} -- 5 };
+ \graph [layered layout, level distance=0cm, nodes=draw]
+ { 1 [x=3,y=2] -- {2,3,4} -- 5[level pre sep=5mm] };
+ \end{tikzpicture}
+ "]]
+}
+
+---
+
+declare {
+ key = "level post sep",
+ type = "length",
+ initial = ".333em",
+
+ summary = [["
+ Works like |level pre sep|.
+ "]]
+}
+
+---
+declare {
+ key = "layer pre sep",
+ type = "length",
+ use = {
+ { key = "level pre sep", value = lib.id },
+ },
+ summary = "An alias for |level pre sep|."
+}
+
+---
+declare {
+ key = "layer post sep",
+ type = "length",
+ use = {
+ { key = "level post sep", value = lib.id },
+ },
+ summary = "An alias for |level post sep|."
+}
+
+
+
+
+---
+-- @param length A length
+
+declare {
+ key = "level sep",
+ type = "length",
+ use = {
+ { key = "level pre sep", value = function (v) return v/2 end },
+ { key = "level post sep", value = function (v) return v/2 end },
+ },
+
+ summary = [["
+ A shorthand for setting both |level pre sep| and |level post sep| to
+ $\meta{length}/2$. Note that if you set |level distance=0| and
+ |level sep=1em|, you get a layout where any two consecutive layers
+ are ``spaced apart'' by |1em|.
+ "]]
+}
+
+
+---
+declare {
+ key = "layer sep",
+ type = "number",
+ use = {
+ { key = "level sep", value = lib.id },
+ },
+ summary = "An alias for |level sep|."
+}
+
+
+---
+declare {
+ key = "sibling distance",
+ type = "length",
+ initial = "1cm",
+
+ summary = [["
+ This is minimum distance that the centers of node should have to the
+ center of the next node on the same level. As for levels, this is
+ just a lower bound.
+ For some layouts, like a simple necklace layout, the \meta{length} is
+ measured as the distance on the circle.
+ "]],
+ examples = {[["
+ \tikz \graph [tree layout, sibling distance=1cm, nodes={circle,draw}]
+ { 1--{2,3,4,5} };
+ "]],[["
+ \tikz \graph [tree layout, sibling distance=0cm, sibling sep=0pt,
+ nodes={circle,draw}]
+ { 1--{2,3,4,5} };
+ "]],[["
+ \tikz \graph [tree layout, sibling distance=0cm, sibling sep=0pt,
+ nodes={circle,draw}]
+ { 1--{2,3[sibling distance=1cm],4,5} };
+ "]]
+ }
+}
+
+
+---
+
+declare {
+ key = "sibling pre sep",
+ type = "length",
+ initial = ".333em",
+
+ summary = [["
+ Works like |level pre sep|, only for siblings.
+ "]],
+ examples = [["
+ \tikz \graph [tree layout, sibling distance=0cm, nodes={circle,draw},
+ sibling sep=0pt]
+ { 1--{2,3[sibling pre sep=1cm],4,5} };
+ "]]
+}
+
+---
+
+declare {
+ key = "sibling post sep",
+ type = "length",
+ initial = ".333em",
+
+ summary = [["
+ Works like |sibling pre sep|.
+ "]]
+ }
+
+
+
+---
+-- @param length A length
+
+declare {
+ key = "sibling sep",
+ type = "length",
+ use = {
+ { key = "sibling pre sep", value = function(v) return v/2 end },
+ { key = "sibling post sep", value = function(v) return v/2 end },
+ },
+
+ summary = [["
+ A shorthand for setting both |sibling pre sep| and |sibling post sep| to
+ $\meta{length}/2$.
+ "]]
+}
+
+
+
+
+
+
+---
+declare {
+ key = "part distance",
+ type = "length",
+ initial = "1.5cm",
+
+ summary = [["
+ This is minimum distance between the centers of ``parts'' of a
+ graph. What a ``part'' is depends on the algorithm.
+ "]]
+}
+
+
+---
+
+declare {
+ key = "part pre sep",
+ type = "length",
+ initial = "1em",
+ summary = "A pre-padding for parts."
+}
+
+---
+
+declare {
+ key = "part post sep",
+ type = "length",
+ initial = "1em",
+ summary = "A post-padding for pars."
+ }
+
+
+
+---
+-- @param length A length
+
+declare {
+ key = "part sep",
+ type = "length",
+ use = {
+ { key = "part pre sep", value = function(v) return v/2 end },
+ { key = "part post sep", value = function(v) return v/2 end },
+ },
+
+ summary = [["
+ A shorthand for setting both |part pre sep| and |part post sep| to
+ $\meta{length}/2$.
+ "]]
+}
+
+
+
+
+---
+
+declare {
+ key = "component sep",
+ type = "length",
+ initial = "1.5em",
+
+ summary = [["
+ This is padding between the bounding boxes that nodes of different
+ connected components will have when they are placed next to each
+ other.
+ "]],
+ examples = {[["
+ \tikz \graph [binary tree layout, sibling distance=4mm, level distance=8mm,
+ components go right top aligned,
+ component sep=1pt, nodes=draw]
+ {
+ 1 -> 2 -> {3->4[second]->5,6,7};
+ a -> b[second] -> c[second] -> d -> e;
+ x -> y[second] -> z -> u[second] -> v;
+ };
+ "]],[["
+ \tikz \graph [binary tree layout, sibling distance=4mm, level distance=8mm,
+ components go right top aligned,
+ component sep=1em, nodes=draw]
+ {
+ 1 -> 2 -> {3->4[second]->5,6,7};
+ a -> b[second] -> c[second] -> d -> e;
+ x -> y[second] -> z -> u[second] -> v;
+ };
+ "]]
+ }
+}
+
+
+
+---
+
+declare {
+ key = "component distance",
+ type = "length",
+ initial = "2cm",
+
+ summary = [["
+ This is the minimum distance between the centers of bounding
+ boxes of connected components when they are placed next to each
+ other. (Not used, currently.)
+ "]]
+}
+
+
+return Distances
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/control/FineTune.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/control/FineTune.lua
new file mode 100644
index 0000000000..87d67b5b56
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/control/FineTune.lua
@@ -0,0 +1,164 @@
+-- Copyright 2012 by Till Tantau
+--
+-- This file may be distributed an/or modified
+--
+-- 1. under the LaTeX Project Public License and/or
+-- 2. under the GNU Public License
+--
+-- See the file doc/generic/pgf/licenses/LICENSE for more information
+
+-- @release $Header$
+
+
+local declare = require "pgf.gd.interface.InterfaceToAlgorithms".declare
+
+local Coordinate = require "pgf.gd.model.Coordinate"
+local lib = require "pgf.gd.lib"
+
+---
+-- @section subsection {Fine-Tuning Positions of Nodes}
+--
+-- @end
+
+
+
+---
+declare {
+ key = "nudge",
+ type = "canvas coordinate",
+
+ summary = [["
+ This option allows you to slightly ``nudge'' (move) nodes after
+ they have been positioned by the given offset. The idea is that
+ this nudging is done after the position of the node has been
+ computed, so nudging has no influence on the actual graph
+ drawing algorithms. This, in turn, means that you can use
+ nudging to ``correct'' or ``optimize'' the positioning of nodes
+ after the algorithm has computed something.
+ "]],
+
+ examples = [["
+ \tikz \graph [edges=rounded corners, nodes=draw,
+ layered layout, sibling distance=0] {
+ a -- {b, c, d[nudge=(up:2mm)]} -- e -- a;
+ };
+ "]]
+}
+
+
+---
+-- @param distance A distance by which the node is nudges.
+
+declare {
+ key = "nudge up",
+ type = "length",
+ use = {
+ { key = "nudge", value = function (v) return Coordinate.new(0,v) end }
+ },
+
+ summary = "A shorthand for nudging a node upwards.",
+ examples = [["
+ \tikz \graph [edges=rounded corners, nodes=draw,
+ layered layout, sibling distance=0] {
+ a -- {b, c, d[nudge up=2mm]} -- e -- a;
+ };
+ "]]
+}
+
+
+---
+-- @param distance A distance by which the node is nudges.
+
+declare {
+ key = "nudge down",
+ type = "length",
+ use = {
+ { key = "nudge", value = function (v) return Coordinate.new(0,-v) end }
+ },
+
+ summary = "Like |nudge up|, but downwards."
+}
+
+---
+-- @param distance A distance by which the node is nudges.
+
+declare {
+ key = "nudge left",
+ type = "length",
+ use = {
+ { key = "nudge", value = function (v) return Coordinate.new(-v,0) end }
+ },
+
+ summary = "Like |nudge up|, but left.",
+ examples = [["
+ \tikz \graph [edges=rounded corners, nodes=draw,
+ layered layout, sibling distance=0] {
+ a -- {b, c, d[nudge left=2mm]} -- e -- a;
+ };
+ "]]
+}
+
+---
+-- @param distance A distance by which the node is nudges.
+
+declare {
+ key = "nudge right",
+ type = "length",
+ use = {
+ { key = "nudge", value = function (v) return Coordinate.new(v,0) end }
+ },
+
+ summary = "Like |nudge left|, but right."
+}
+
+
+---
+declare {
+ key = "regardless at",
+ type = "canvas coordinate",
+
+ summary = [["
+ Using this option you can provide a position for a node to wish
+ it will be forced after the graph algorithms have run. So, the node
+ is positioned normally and the graph drawing algorithm does not know
+ about the position specified using |regardless at|. However,
+ afterwards, the node is placed there, regardless of what the
+ algorithm has computed (all other nodes are unaffected).
+ "]],
+ examples = [["
+ \tikz \graph [edges=rounded corners, nodes=draw,
+ layered layout, sibling distance=0] {
+ a -- {b,c,d[regardless at={(1,0)}]} -- e -- a;
+ };
+ "]]
+}
+
+
+
+
+---
+-- @param pos A canvas position (a coordinate).
+
+declare {
+ key = "nail at",
+ type = "canvas coordinate",
+ use = {
+ { key = "desired at", value = lib.id },
+ { key = "regardless at", value = lib.id },
+ },
+
+ summary = [["
+ This option combines |desired at| and |regardless at|. Thus, the
+ algorithm is ``told'' about the desired position. If it fails to place
+ the node at the desired position, it will be put there
+ regardless. The name of the key is intended to remind one of a node
+ being ``nailed'' to the canvas.
+ "]],
+ examples = [["
+ \tikz \graph [edges=rounded corners, nodes=draw,
+ layered layout, sibling distance=0] {
+ a -- {b,c,d[nail at={(1,0)}]} -- e[nail at={(1.5,-1)}] -- a;
+ };
+ "]]
+}
+
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/control/LayoutPipeline.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/control/LayoutPipeline.lua
new file mode 100644
index 0000000000..1c5e48b4c7
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/control/LayoutPipeline.lua
@@ -0,0 +1,1347 @@
+-- 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$
+
+
+---
+-- This class controls the running of graph drawing algorithms on
+-- graphs. In particular, it performs pre- and posttransformations and
+-- also invokes the collapsing of sublayouts.
+--
+-- You do not call any of the methods of this class directly, the
+-- whole class is included only for documentation purposes.
+--
+-- Before an algorithm is applied, a number of transformations will
+-- have been applied, depending on the algorithm's |preconditions|
+-- field:
+-- %
+-- \begin{itemize}
+-- \item |connected|
+--
+-- If this property is set for an algorithm (that is, in the
+-- |declare| statement for the algorithm the |predconditions| field
+-- has the entry |connected=true| set), then the graph will be
+-- decomposed into connected components. The algorithm is run on each
+-- component individually.
+-- \item |tree|
+--
+-- When set, the field |spanning_tree| of the algorithm will be set
+-- to a spanning tree of the graph. This option implies |connected|.
+-- \item |loop_free|
+--
+-- When set, all loops (arcs from a vertex to itself) will have been
+-- removed when the algorithm runs.
+--
+-- \item |at_least_two_nodes|
+--
+-- When explicitly set to |false| (this precondition is |true| by
+-- default), the algorithm will even be run if there is only a
+-- single vertex in the graph.
+-- \end{itemize}
+--
+-- Once the algorithm has run, the algorithm's |postconditions| will
+-- be processed:
+-- %
+-- \begin{itemize}
+-- \item |upward_oriented|
+--
+-- When set, the algorithm tells the layout pipeline that the graph
+-- has been laid out in a layered manner with each layer going from
+-- left to right and layers at a whole going upwards (positive
+-- $y$-coordinates). The graph will then be rotated and possibly
+-- swapped in accordance with the |grow| key set by the user.
+-- \item |fixed|
+--
+-- When set, no rotational postprocessing will be done after the
+-- algorithm has run. Usually, a graph is rotated to meet a user's
+-- |orient| settings. However, when the algorithm has already
+-- ``ideally'' rotated the graph, set this postcondition.
+-- \end{itemize}
+--
+--
+-- 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 = {}
+
+
+-- Namespace
+require("pgf.gd.control").LayoutPipeline = LayoutPipeline
+
+
+-- Imports
+local Direct = require "pgf.gd.lib.Direct"
+local Storage = require "pgf.gd.lib.Storage"
+local Simplifiers = require "pgf.gd.lib.Simplifiers"
+local LookupTable = require "pgf.gd.lib.LookupTable"
+local Transform = require "pgf.gd.lib.Transform"
+
+local Arc = require "pgf.gd.model.Arc"
+local Vertex = require "pgf.gd.model.Vertex"
+local Digraph = require "pgf.gd.model.Digraph"
+local Coordinate = require "pgf.gd.model.Coordinate"
+local Path = require "pgf.gd.model.Path"
+
+local Sublayouts = require "pgf.gd.control.Sublayouts"
+
+local lib = require "pgf.gd.lib"
+
+local InterfaceCore = require "pgf.gd.interface.InterfaceCore"
+
+
+
+
+-- Forward definitions
+
+local prepare_events
+
+
+
+-- The main ``graph drawing pipeline'' that handles the pre- and
+-- postprocessing for a graph. This method is called by the display
+-- interface.
+--
+-- @param scope A graph drawing scope.
+
+function LayoutPipeline.run(scope)
+
+ -- The pipeline...
+
+ -- Step 1: Preparations
+
+ -- Prepare events
+ prepare_events(scope.events)
+
+ -- Step 2: Recursively layout the graph, starting with the root layout
+ local root_layout = assert(scope.collections[InterfaceCore.sublayout_kind][1], "no layout in scope")
+
+ scope.syntactic_digraph =
+ Sublayouts.layoutRecursively (scope,
+ root_layout,
+ LayoutPipeline.runOnLayout,
+ { root_layout })
+
+ -- Step 3: Anchor the graph
+ LayoutPipeline.anchor(scope.syntactic_digraph, scope)
+
+ -- Step 4: Apply regardless transforms
+ Sublayouts.regardless(scope.syntactic_digraph)
+
+ -- Step 5: Cut edges
+ LayoutPipeline.cutEdges(scope.syntactic_digraph)
+
+end
+
+
+
+--
+-- This method is called by the sublayout rendering pipeline when the
+-- algorithm should be invoked for an individual graph. At this point,
+-- the sublayouts will already have been collapsed.
+--
+-- @param scope The graph drawing scope.
+-- @param algorithm_class The to-be-applied algorithm class.
+-- @param layout_graph A subgraph of the syntactic digraph which is
+-- restricted to the current layout and in which sublayouts have
+-- been contracted to single nodes.
+-- @param layout The layout to which the graph belongs.
+--
+function LayoutPipeline.runOnLayout(scope, algorithm_class, layout_graph, layout)
+
+ if #layout_graph.vertices < 1 then
+ return
+ end
+
+ -- The involved main graphs:
+ local layout_copy = Digraph.new (layout_graph) --Direct.digraphFromSyntacticDigraph(layout_graph)
+ for _,a in ipairs(layout_graph.arcs) do
+ local new_a = layout_copy:connect(a.tail,a.head)
+ new_a.syntactic_edges = a.syntactic_edges
+ end
+
+ -- Step 1: Decompose the graph into connected components, if necessary:
+ local syntactic_components
+ if algorithm_class.preconditions.tree or algorithm_class.preconditions.connected or layout_graph.options.componentwise then
+ syntactic_components = LayoutPipeline.decompose(layout_copy)
+ LayoutPipeline.sortComponents(layout_graph.options['component order'], syntactic_components)
+ else
+ -- Only one component: The graph itself...
+ syntactic_components = { layout_copy }
+ end
+
+ -- Step 2: For all components do:
+ for i,syntactic_component in ipairs(syntactic_components) do
+
+ -- Step 2.1: Reset random number generator to make sure that the
+ -- same graph is always typeset in the same way.
+ lib.randomseed(layout_graph.options['random seed'])
+
+ local digraph = Direct.digraphFromSyntacticDigraph(syntactic_component)
+
+ -- Step 2.3: If requested, remove loops
+ if algorithm_class.preconditions.loop_free then
+ for _,v in ipairs(digraph.vertices) do
+ digraph:disconnect(v,v)
+ end
+ end
+
+ -- Step 2.4: Precompute the underlying undirected graph
+ local ugraph = Direct.ugraphFromDigraph(digraph)
+
+ -- Step 2.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{
+ digraph = digraph,
+ ugraph = ugraph,
+ scope = scope,
+ layout = layout,
+ layout_graph = layout_graph,
+ syntactic_component = syntactic_component,
+ }
+
+ -- Step 2.7: Compute a spanning tree, if necessary
+ if algorithm_class.preconditions.tree then
+ local spanning_algorithm_class = syntactic_component.options.algorithm_phases["spanning tree computation"]
+ algorithm.spanning_tree =
+ spanning_algorithm_class.new{
+ ugraph = ugraph,
+ events = scope.events
+ }:run()
+ end
+
+ -- Step 2.8: Compute growth-adjusted sizes
+ algorithm.rotation_info = LayoutPipeline.prepareRotateAround(algorithm.postconditions, syntactic_component)
+ algorithm.adjusted_bb = Storage.newTableStorage()
+ LayoutPipeline.prepareBoundingBoxes(algorithm.rotation_info, algorithm.adjusted_bb, syntactic_component, syntactic_component.vertices)
+
+ -- Step 2.9: Finally, run algorithm on this component!
+ if #digraph.vertices > 1 or algorithm_class.run_also_for_single_node
+ or algorithm_class.preconditions.at_least_two_nodes == false then
+ -- Main run of the algorithm:
+ if algorithm_class.old_graph_model then
+ LayoutPipeline.runOldGraphModel(scope, digraph, algorithm_class, algorithm)
+ else
+ algorithm:run ()
+ end
+ end
+
+ -- Step 2.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()
+ if algorithm.spanning_tree then
+ algorithm.spanning_tree:sync()
+ end
+
+ -- Step 2.11: Orient the graph
+ LayoutPipeline.orient(algorithm.rotation_info, algorithm.postconditions, syntactic_component, scope)
+ end
+
+ -- Step 3: Packing:
+ LayoutPipeline.packComponents(layout_graph, syntactic_components)
+
+end
+
+
+
+
+
+
+---
+-- This function is called internally to perform the graph anchoring
+-- procedure described in
+-- Section~\ref{subsection-library-graphdrawing-anchoring}. These
+-- transformations are always performed.
+--
+-- @param graph A graph
+-- @param scope The scope
+
+function LayoutPipeline.anchor(graph, scope)
+
+ -- Step 1: Find anchor node:
+ local anchor_node
+
+ local anchor_node_name = graph.options['anchor node']
+ if anchor_node_name then
+ anchor_node = scope.node_names[anchor_node_name]
+ end
+
+ if not graph:contains(anchor_node) then
+ anchor_node =
+ lib.find (graph.vertices, function (v) return v.options['anchor here'] end) or
+ lib.find (graph.vertices, function (v) return v.options['desired at'] end) or
+ graph.vertices[1]
+ end
+
+ -- Sanity check
+ assert(graph:contains(anchor_node), "anchor node is not in graph!")
+
+ local desired = anchor_node.options['desired at'] or graph.options['anchor at']
+ local delta = desired - anchor_node.pos
+
+ -- Step 3: Shift nodes
+ for _,v in ipairs(graph.vertices) do
+ v.pos:shiftByCoordinate(delta)
+ end
+ for _,a in ipairs(graph.arcs) do
+ if a.path then a.path:shiftByCoordinate(delta) end
+ for _,e in ipairs(a.syntactic_edges) do
+ e.path:shiftByCoordinate(delta)
+ end
+ end
+end
+
+
+
+---
+-- This method tries to determine in which direction the graph is supposed to
+-- grow and in which direction the algorithm will grow the graph. These two
+-- pieces of information together produce a necessary rotation around some node.
+-- This rotation is returned in a table.
+--
+-- Note that this method does not actually cause a rotation to happen; this is
+-- left to other method.
+--
+-- @param postconditions The algorithm's postconditions.
+-- @param graph An undirected graph
+-- @return A table containing the computed information.
+
+function LayoutPipeline.prepareRotateAround(postconditions, graph)
+
+ -- Find the vertex from which we orient
+ local swap = true
+
+ local v,_,grow = lib.find (graph.vertices, function (v) return v.options["grow"] end)
+
+ if not v and graph.options["grow"] then
+ v,grow,swap = graph.vertices[1], graph.options["grow"], true
+ end
+
+ if not v then
+ v,_,grow = lib.find (graph.vertices, function (v) return v.options["grow'"] end)
+ swap = false
+ end
+
+ if not v and graph.options["grow'"] then
+ v,grow,swap = graph.vertices[1], graph.options["grow'"], false
+ end
+
+ if not v then
+ v, grow, swap = graph.vertices[1], -90, true
+ end
+
+ -- Now compute the rotation
+ local info = {}
+ local growth_direction = (postconditions.upward_oriented and 90) or (postconditions.upward_oriented_swapped and 90)
+
+ if postconditions.upward_oriented_swapped then
+ swap = not swap
+ end
+
+ if growth_direction == "fixed" then
+ info.angle = 0 -- no rotation
+ elseif growth_direction then
+ info.from_node = v
+ info.from_angle = growth_direction/360*2*math.pi
+ info.to_angle = grow/360*2*math.pi
+ info.swap = swap
+ info.angle = info.to_angle - info.from_angle
+ else
+ info.from_node = v
+ local other = lib.find_min(
+ graph:outgoing(v),
+ function (a)
+ if a.head ~= v and a:eventIndex() then
+ return a, a:eventIndex()
+ end
+ end)
+ info.to_node = (other and other.head) or
+ (graph.vertices[1] == v and graph.vertices[2] or graph.vertices[1])
+ info.to_angle = grow/360*2*math.pi
+ info.swap = swap
+ info.angle = info.to_angle - math.atan2(info.to_node.pos.y - v.pos.y, info.to_node.pos.x - v.pos.x)
+ end
+
+ return info
+end
+
+
+
+---
+-- Compute growth-adjusted node sizes.
+--
+-- For each node of the graph, compute bounding box of the node that
+-- results when the node is rotated so that it is in the correct
+-- orientation for what the algorithm assumes.
+--
+-- The ``bounding box'' actually consists of the fields
+-- %
+-- \begin{itemize}
+-- \item |sibling_pre|,
+-- \item |sibling_post|,
+-- \item |layer_pre|, and
+-- \item |layer_post|,
+-- \end{itemize}
+-- %
+-- which correspond to ``min x'', ``min y'', ``min y'', and ``max y''
+-- for a tree growing up.
+--
+-- The computation of the ``bounding box'' treats a centered circle in
+-- a special way, all other shapes are currently treated like a
+-- rectangle.
+--
+-- @param rotation_info The table computed by the function prepareRotateAround
+-- @param packing_storage A storage in which the computed distances are stored.
+-- @param graph An graph
+-- @param vertices An array of to-be-prepared vertices inside graph
+
+function LayoutPipeline.prepareBoundingBoxes(rotation_info, adjusted_bb, graph, vertices)
+
+ local angle = assert(rotation_info.angle, "angle field missing")
+ local swap = rotation_info.swap
+
+ for _,v in ipairs(vertices) do
+ local bb = adjusted_bb[v]
+ local a = angle
+
+ if v.shape == "circle" then
+ a = 0 -- no rotation for circles.
+ end
+
+ -- Fill the bounding box field,
+ bb.sibling_pre = math.huge
+ bb.sibling_post = -math.huge
+ bb.layer_pre = math.huge
+ bb.layer_post = -math.huge
+
+ local c = math.cos(angle)
+ local s = math.sin(angle)
+ for _,p in ipairs(v.path:coordinates()) do
+ local x = p.x*c + p.y*s
+ local y = -p.x*s + p.y*c
+
+ bb.sibling_pre = math.min (bb.sibling_pre, x)
+ bb.sibling_post = math.max (bb.sibling_post, x)
+ bb.layer_pre = math.min (bb.layer_pre, y)
+ bb.layer_post = math.max (bb.layer_post, y)
+ end
+
+ -- Flip sibling per and post if flag:
+ if swap then
+ bb.sibling_pre, bb.sibling_post = -bb.sibling_post, -bb.sibling_pre
+ end
+ end
+end
+
+
+
+
+
+--
+-- Rotate the whole graph around a point
+--
+-- Causes the graph to be rotated around \meta{around} so that what
+-- used to be the |from_angle| becomes the |to_angle|. If the flag |swap|
+-- is set, the graph is additionally swapped along the |to_angle|.
+--
+-- @param graph The to-be-rotated (undirected) graph
+-- @param around_x The $x$-coordinate of the point around which the graph should be rotated
+-- @param around_y The $y$-coordinate
+-- @param from An ``old'' angle
+-- @param to A ``new'' angle
+-- @param swap A boolean that, when true, requests that the graph is
+-- swapped (flipped) along the new angle
+
+function LayoutPipeline.rotateGraphAround(graph, around_x, around_y, from, to, swap)
+
+ -- Translate to origin
+ local t = Transform.new_shift(-around_x, -around_y)
+
+ -- Rotate to zero degrees:
+ t = Transform.concat(Transform.new_rotation(-from), t)
+
+ -- Swap
+ if swap then
+ t = Transform.concat(Transform.new_scaling(1,-1), t)
+ end
+
+ -- Rotate to from degrees:
+ t = Transform.concat(Transform.new_rotation(to), t)
+
+ -- Translate back
+ t = Transform.concat(Transform.new_shift(around_x, around_y), t)
+
+ for _,v in ipairs(graph.vertices) do
+ v.pos:apply(t)
+ end
+ for _,a in ipairs(graph.arcs) do
+ for _,p in ipairs(a:pointCloud()) do
+ p:apply(t)
+ end
+ end
+end
+
+
+
+--
+-- Orient the whole graph using two nodes
+--
+-- The whole graph is rotated so that the line from the first node to
+-- the second node has the given angle. If swap is set to true, the
+-- graph is also flipped along this line.
+--
+-- @param graph
+-- @param first_node
+-- @param seond_node
+-- @param target_angle
+-- @param swap
+
+function LayoutPipeline.orientTwoNodes(graph, first_node, second_node, target_angle, swap)
+ if first_node and second_node then
+ -- Compute angle between first_node and second_node:
+ local x = second_node.pos.x - first_node.pos.x
+ local y = second_node.pos.y - first_node.pos.y
+
+ local angle = math.atan2(y,x)
+ LayoutPipeline.rotateGraphAround(graph, first_node.pos.x,
+ first_node.pos.y, angle, target_angle, swap)
+ end
+end
+
+
+
+---
+-- Performs a post-layout orientation of the graph by performing the
+-- steps documented in Section~\ref{subsection-library-graphdrawing-standard-orientation}.
+--
+-- @param rotation_info The info record computed by the function |prepareRotateAround|.
+-- @param postconditions The algorithm's postconditions.
+-- @param graph A to-be-oriented graph.
+-- @param scope The graph drawing scope.
+
+function LayoutPipeline.orient(rotation_info, postconditions, graph, scope)
+
+ -- Sanity check
+ if #graph.vertices < 2 then return end
+
+ -- Step 1: Search for global graph orient options:
+ local function f (orient, tail, head, flag)
+ if orient and head and tail then
+ local n1 = scope.node_names[tail]
+ local n2 = scope.node_names[head]
+ if graph:contains(n1) and graph:contains(n2) then
+ LayoutPipeline.orientTwoNodes(graph, n1, n2, orient/360*2*math.pi, flag)
+ return true
+ end
+ end
+ end
+ if f(graph.options["orient"], graph.options["orient tail"],graph.options["orient head"], false) then return end
+ if f(graph.options["orient'"], graph.options["orient tail"],graph.options["orient head"], true) then return end
+ local tail, head = string.match(graph.options["horizontal"] or "", "^(.*) to (.*)$")
+ if f(0, tail, head, false) then return end
+ local tail, head = string.match(graph.options["horizontal'"] or "", "^(.*) to (.*)$")
+ if f(0, tail, head, true) then return end
+ local tail, head = string.match(graph.options["vertical"] or "", "^(.*) to (.*)$")
+ if f(-90, tail, head, false) then return end
+ local tail, head = string.match(graph.options["vertical'"] or "", "^(.*) to (.*)$")
+ if f(-90, tail, head, true) then return end
+
+ -- Step 2: Search for a node with the orient option:
+ for _, v in ipairs(graph.vertices) do
+ local function f (key, flag)
+ local orient = v.options[key]
+ local head = v.options["orient head"]
+ local tail = v.options["orient tail"]
+
+ if orient and head then
+ local n2 = scope.node_names[head]
+ if graph:contains(n2) then
+ LayoutPipeline.orientTwoNodes(graph, v, n2, orient/360*2*math.pi, flag)
+ return true
+ end
+ elseif orient and tail then
+ local n1 = scope.node_names[tail]
+ if graph:contains(n1) then
+ LayoutPipeline.orientTwoNodes(graph, n1, v, orient/360*2*math.pi, flag)
+ return true
+ end
+ end
+ end
+ if f("orient", false) then return end
+ if f("orient'", true) then return end
+ end
+
+ -- Step 3: Search for an edge with the orient option:
+ for _, a in ipairs(graph.arcs) do
+ if a:options("orient",true) then
+ return LayoutPipeline.orientTwoNodes(graph, a.tail, a.head, a:options("orient")/360*2*math.pi, false)
+ end
+ if a:options("orient'",true) then
+ return LayoutPipeline.orientTwoNodes(graph, a.tail, a.head, a:options("orient'")/360*2*math.pi, true)
+ end
+ end
+
+ -- Step 4: Search two nodes with a desired at option:
+ local first, second, third
+
+ for _, v in ipairs(graph.vertices) do
+ if v.options['desired at'] then
+ if first then
+ if second then
+ third = v
+ break
+ else
+ second = v
+ end
+ else
+ first = v
+ end
+ end
+ end
+
+ if second then
+ local a = first.options['desired at']
+ local b = second.options['desired at']
+ return LayoutPipeline.orientTwoNodes(graph, first, second, math.atan2(b.y-a.y,b.x-a.x), false)
+ end
+
+ -- Computed during preprocessing:
+ if rotation_info.from_node and postconditions.fixed ~= true then
+ local x = rotation_info.from_node.pos.x
+ local y = rotation_info.from_node.pos.y
+ local from_angle = rotation_info.from_angle or math.atan2(rotation_info.to_node.pos.y - y, rotation_info.to_node.pos.x - x)
+
+ LayoutPipeline.rotateGraphAround(graph, x, y, from_angle, rotation_info.to_angle, rotation_info.swap)
+ end
+end
+
+
+
+
+---
+-- This internal function is called to decompose a graph into its
+-- components. Whether or not this function is called depends on
+-- whether the precondition |connected| is set for the algorithm class
+-- and whether the |componentwise| key is used.
+--
+-- @param graph A to-be-decomposed graph
+--
+-- @return An array of graph objects that represent the connected components of the graph.
+
+function LayoutPipeline.decompose (digraph)
+
+ -- The list of connected components (node sets)
+ local components = {}
+
+ -- Remember, which graphs have already been visited
+ local visited = {}
+
+ for _,v in ipairs(digraph.vertices) do
+ if not visited[v] then
+ -- Start a depth-first-search of the graph, starting at node n:
+ local stack = { v }
+ local component = Digraph.new {
+ syntactic_digraph = digraph.syntactic_digraph,
+ options = digraph.options
+ }
+
+ while #stack >= 1 do
+ local tos = stack[#stack]
+ stack[#stack] = nil -- pop
+
+ if not visited[tos] then
+
+ -- Visit pos:
+ component:add { tos }
+ visited[tos] = true
+
+ -- Push all unvisited neighbors:
+ for _,a in ipairs(digraph:incoming(tos)) do
+ local neighbor = a.tail
+ if not visited[neighbor] then
+ stack[#stack+1] = neighbor -- push
+ end
+ end
+ for _,a in ipairs(digraph:outgoing(tos)) do
+ local neighbor = a.head
+ if not visited[neighbor] then
+ stack[#stack+1] = neighbor -- push
+ end
+ end
+ end
+ end
+
+ -- Ok, vertices will now contain all vertices reachable from n.
+ components[#components+1] = component
+ end
+ end
+
+ if #components < 2 then
+ return { digraph }
+ end
+
+ for _,c in ipairs(components) do
+ table.sort (c.vertices, function (u,v) return u.event.index < v.event.index end)
+ for _,v in ipairs(c.vertices) do
+ for _,a in ipairs(digraph:outgoing(v)) do
+ local new_a = c:connect(a.tail, a.head)
+ new_a.syntactic_edges = a.syntactic_edges
+ end
+ for _,a in ipairs(digraph:incoming(v)) do
+ local new_a = c:connect(a.tail, a.head)
+ new_a.syntactic_edges = a.syntactic_edges
+ end
+ end
+ end
+
+ return components
+end
+
+
+
+
+-- Handling of component order
+--
+-- LayoutPipeline are ordered according to a function that is stored in
+-- a key of the |LayoutPipeline.component_ordering_functions| table
+-- whose name is the graph option |component order|.
+--
+-- @param component_order An ordering method
+-- @param subgraphs A list of to-be-sorted subgraphs
+
+function LayoutPipeline.sortComponents(component_order, subgraphs)
+ if component_order then
+ local f = LayoutPipeline.component_ordering_functions[component_order]
+ if f then
+ table.sort (subgraphs, f)
+ end
+ end
+end
+
+
+-- Right now, we hardcode the functions here. Perhaps make this
+-- dynamic in the future. Could easily be done on the tikzlayer,
+-- actually.
+
+LayoutPipeline.component_ordering_functions = {
+ ["increasing node number"] =
+ function (g,h)
+ if #g.vertices == #h.vertices then
+ return g.vertices[1].event.index < h.vertices[1].event.index
+ else
+ return #g.vertices < #h.vertices
+ end
+ end,
+ ["decreasing node number"] =
+ function (g,h)
+ if #g.vertices == #h.vertices then
+ return g.vertices[1].event.index < h.vertices[1].event.index
+ else
+ return #g.vertices > #h.vertices
+ end
+ end,
+ ["by first specified node"] = nil,
+}
+
+
+
+
+local function compute_rotated_bb(vertices, angle, sep, bb)
+
+ local r = Transform.new_rotation(-angle)
+
+ for _,v in ipairs(vertices) do
+ -- Find the rotated bounding box field,
+ local t = Transform.concat(r,Transform.new_shift(v.pos.x, v.pos.y))
+
+ local min_x = math.huge
+ local max_x = -math.huge
+ local min_y = math.huge
+ local max_y = -math.huge
+
+ for _,e in ipairs(v.path) do
+ if type(e) == "table" then
+ local c = e:clone()
+ c:apply(t)
+
+ min_x = math.min (min_x, c.x)
+ max_x = math.max (max_x, c.x)
+ min_y = math.min (min_y, c.y)
+ max_y = math.max (max_y, c.y)
+ end
+ end
+
+ -- Enlarge by sep:
+ min_x = min_x - sep
+ max_x = max_x + sep
+ min_y = min_y - sep
+ max_y = max_y + sep
+
+ local _,_,_,_,c_x,c_y = v:boundingBox()
+ local center = Coordinate.new(c_x,c_y)
+
+ center:apply(t)
+
+ bb[v].min_x = min_x
+ bb[v].max_x = max_x
+ bb[v].min_y = min_y
+ bb[v].max_y = max_y
+ bb[v].c_y = center.y
+ end
+end
+
+
+
+---
+-- This internal function packs the components of a graph. See
+-- Section~\ref{subsection-gd-component-packing} for details.
+--
+-- @param graph The graph
+-- @param components A list of components
+
+function LayoutPipeline.packComponents(syntactic_digraph, components)
+
+ local vertices = Storage.newTableStorage()
+ local bb = Storage.newTableStorage()
+
+ -- Step 1: Preparation, rotation to target direction
+ local sep = syntactic_digraph.options['component sep']
+ local angle = syntactic_digraph.options['component direction']/180*math.pi
+
+ local mark = {}
+ for _,c in ipairs(components) do
+
+ -- Setup the lists of to-be-considered nodes
+ local vs = {}
+ for _,v in ipairs(c.vertices) do
+ vs [#vs + 1] = v
+ end
+
+ for _,a in ipairs(c.arcs) do
+ for _,p in ipairs(a:pointCloud()) do
+ vs [#vs + 1] = Vertex.new { pos = p }
+ end
+ end
+ vertices[c] = vs
+
+ compute_rotated_bb(vs, angle, sep/2, bb)
+ end
+
+ local x_shifts = { 0 }
+ local y_shifts = {}
+
+ -- Step 2: Vertical alignment
+ for i,c in ipairs(components) do
+ local max_max_y = -math.huge
+ local max_center_y = -math.huge
+ local min_min_y = math.huge
+ local min_center_y = math.huge
+
+ for _,v in ipairs(c.vertices) do
+ local info = bb[v]
+ max_max_y = math.max(info.max_y, max_max_y)
+ max_center_y = math.max(info.c_y, max_center_y)
+ min_min_y = math.min(info.min_y, min_min_y)
+ min_center_y = math.min(info.c_y, min_center_y)
+ end
+
+ -- Compute alignment line
+ local valign = syntactic_digraph.options['component align']
+ local line
+ if valign == "counterclockwise bounding box" then
+ line = max_max_y
+ elseif valign == "counterclockwise" then
+ line = max_center_y
+ elseif valign == "center" then
+ line = (max_max_y + min_min_y) / 2
+ elseif valign == "clockwise" then
+ line = min_center_y
+ elseif valign == "first node" then
+ line = bb[c.vertices[1]].c_y
+ else
+ line = min_min_y
+ end
+
+ -- Overruled?
+ for _,v in ipairs(c.vertices) do
+ if v.options['align here'] then
+ line = bb[v].c_y
+ break
+ end
+ end
+
+ -- Ok, go!
+ y_shifts[i] = -line
+
+ -- Adjust nodes:
+ for _,v in ipairs(vertices[c]) do
+ local info = bb[v]
+ info.min_y = info.min_y - line
+ info.max_y = info.max_y - line
+ info.c_y = info.c_y - line
+ end
+ end
+
+ -- Step 3: Horizontal alignment
+ local y_values = {}
+
+ for _,c in ipairs(components) do
+ for _,v in ipairs(vertices[c]) do
+ local info = bb[v]
+ y_values[#y_values+1] = info.min_y
+ y_values[#y_values+1] = info.max_y
+ y_values[#y_values+1] = info.c_y
+ end
+ end
+
+ table.sort(y_values)
+
+ local y_ranks = {}
+ local right_face = {}
+ for i=1,#y_values do
+ y_ranks[y_values[i]] = i
+ right_face[i] = -math.huge
+ end
+
+
+
+ for i=1,#components-1 do
+ -- First, update right_face:
+ local touched = {}
+
+ for _,v in ipairs(vertices[components[i]]) do
+ local info = bb[v]
+ local border = info.max_x
+
+ for i=y_ranks[info.min_y],y_ranks[info.max_y] do
+ touched[i] = true
+ right_face[i] = math.max(right_face[i], border)
+ end
+ end
+
+ -- Fill up the untouched entries:
+ local right_max = -math.huge
+ for i=1,#y_values do
+ if not touched[i] then
+ -- Search for next and previous touched
+ local interpolate = -math.huge
+ for j=i+1,#y_values do
+ if touched[j] then
+ interpolate = math.max(interpolate,right_face[j] - (y_values[j] - y_values[i]))
+ break
+ end
+ end
+ for j=i-1,1,-1 do
+ if touched[j] then
+ interpolate = math.max(interpolate,right_face[j] - (y_values[i] - y_values[j]))
+ break
+ end
+ end
+ right_face[i] = math.max(interpolate,right_face[i])
+ end
+ right_max = math.max(right_max, right_face[i])
+ end
+
+ -- Second, compute the left face
+ local touched = {}
+ local left_face = {}
+ for i=1,#y_values do
+ left_face[i] = math.huge
+ end
+ for _,v in ipairs(vertices[components[i+1]]) do
+ local info = bb[v]
+ local border = info.min_x
+
+ for i=y_ranks[info.min_y],y_ranks[info.max_y] do
+ touched[i] = true
+ left_face[i] = math.min(left_face[i], border)
+ end
+ end
+
+ -- Fill up the untouched entries:
+ local left_min = math.huge
+ for i=1,#y_values do
+ if not touched[i] then
+ -- Search for next and previous touched
+ local interpolate = math.huge
+ for j=i+1,#y_values do
+ if touched[j] then
+ interpolate = math.min(interpolate,left_face[j] + (y_values[j] - y_values[i]))
+ break
+ end
+ end
+ for j=i-1,1,-1 do
+ if touched[j] then
+ interpolate = math.min(interpolate,left_face[j] + (y_values[i] - y_values[j]))
+ break
+ end
+ end
+ left_face[i] = interpolate
+ end
+ left_min = math.min(left_min, left_face[i])
+ end
+
+ -- Now, compute the shift.
+ local shift = -math.huge
+
+ if syntactic_digraph.options['component packing'] == "rectangular" then
+ shift = right_max - left_min
+ else
+ for i=1,#y_values do
+ shift = math.max(shift, right_face[i] - left_face[i])
+ end
+ end
+
+ -- Adjust nodes:
+ x_shifts[i+1] = shift
+ for _,v in ipairs(vertices[components[i+1]]) do
+ local info = bb[v]
+ info.min_x = info.min_x + shift
+ info.max_x = info.max_x + shift
+ end
+ end
+
+ -- Now, rotate shifts
+ for i,c in ipairs(components) do
+ local x = x_shifts[i]*math.cos(angle) - y_shifts[i]*math.sin(angle)
+ local y = x_shifts[i]*math.sin(angle) + y_shifts[i]*math.cos(angle)
+
+ for _,v in ipairs(vertices[c]) do
+ v.pos.x = v.pos.x + x
+ v.pos.y = v.pos.y + y
+ end
+ end
+end
+
+
+
+
+
+
+
+--
+-- Store for each begin/end event the index of
+-- its corresponding end/begin event
+--
+-- @param events An event list
+
+prepare_events =
+ function (events)
+ local stack = {}
+
+ for i=1,#events do
+ if events[i].kind == "begin" then
+ stack[#stack + 1] = i
+ elseif events[i].kind == "end" then
+ local tos = stack[#stack]
+ stack[#stack] = nil -- pop
+
+ events[tos].end_index = i
+ events[i].begin_index = tos
+ end
+ end
+ end
+
+
+
+---
+-- Cut the edges. This function handles the ``cutting'' of edges. The
+-- idea is that every edge is a path going from the center of the from
+-- node to the center of the target node. Now, we intersect this path
+-- with the path of the start node and cut away everything before this
+-- intersection. Likewise, we intersect the path with the head node
+-- and, again, cut away everything following the intersection.
+--
+-- These cuttings are not done if appropriate options are set.
+
+function LayoutPipeline.cutEdges(graph)
+
+ for _,a in ipairs(graph.arcs) do
+ for _,e in ipairs(a.syntactic_edges) do
+ local p = e.path
+ p:makeRigid()
+ local orig = p:clone()
+
+ if e.options['tail cut'] and e.tail.options['cut policy'] == "as edge requests"
+ or e.tail.options['cut policy'] == "all" then
+
+ local vpath = e.tail.path:clone()
+ vpath:shiftByCoordinate(e.tail.pos)
+
+ local x = p:intersectionsWith (vpath)
+
+ if #x > 0 then
+ p:cutAtBeginning(x[1].index, x[1].time)
+ end
+ end
+
+ if e.options['head cut'] and e.head.options['cut policy'] == "as edge requests"
+ or e.head.options['cut policy'] == "all" then
+
+ local vpath = e.head.path:clone()
+ vpath:shiftByCoordinate(e.head.pos)
+ x = p:intersectionsWith (vpath)
+ if #x > 0 then
+ p:cutAtEnd(x[#x].index, x[#x].time)
+ else
+ -- Check whether there was an intersection with the original
+ --path:
+ local x2 = orig:intersectionsWith (vpath)
+ if #x2 > 0 then
+ -- Ok, after cutting the tail vertex, there is no longer
+ -- an intersection with the head vertex, but there used to
+ -- be one. This means that the vertices overlap and the
+ -- path should be ``inside'' them. Hmm...
+ if e.options['allow inside edges'] and #p > 1 then
+ local from = p[2]
+ local to = x2[1].point
+ p:clear()
+ p:appendMoveto(from)
+ p:appendLineto(to)
+ else
+ p:clear()
+ end
+ end
+ end
+ end
+ end
+ end
+end
+
+
+
+
+
+
+-- Deprecated stuff
+
+local Node = require "pgf.gd.deprecated.Node"
+local Graph = require "pgf.gd.deprecated.Graph"
+local Edge = require "pgf.gd.deprecated.Edge"
+local Cluster = require "pgf.gd.deprecated.Cluster"
+
+
+
+
+
+local unique_count = 0
+
+local function compatibility_digraph_to_graph(scope, g)
+ local graph = Graph.new()
+
+ -- Graph options
+ graph.options = g.options
+ graph.orig_digraph = g
+
+ -- Events
+ for i,e in ipairs(scope.events) do
+ graph.events[i] = e
+ end
+
+ -- Nodes
+ for _,v in ipairs(g.vertices) do
+ if not v.name then
+ -- compat needs unique name
+ v.name = "auto generated node nameINTERNAL" .. unique_count
+ unique_count = unique_count + 1
+ end
+ local minX, minY, maxX, maxY = v:boundingBox()
+ local node = Node.new{
+ name = v.name,
+ tex = {
+ tex_node = v.tex and v.tex.stored_tex_box_number,
+ shape = v.shape,
+ minX = minX,
+ maxX = maxX,
+ minY = minY,
+ maxY = maxY,
+ },
+ options = v.options,
+ event_index = v.event.index,
+ index = v.event.index,
+ orig_vertex = v,
+ }
+ graph:addNode(node)
+ graph.events[v.event.index or (#graph.events+1)] = { kind = 'node', parameters = node }
+ end
+
+ -- Edges
+ local mark = Storage.new()
+ for _,a in ipairs(g.arcs) do
+ local da = g.syntactic_digraph:arc(a.tail, a.head)
+ if da then
+ for _,m in ipairs(da.syntactic_edges) do
+ if not mark[m] then
+ mark[m] = true
+ local from_node = graph:findNode(da.tail.name)
+ local to_node = graph:findNode(da.head.name)
+ local edge = graph:createEdge(from_node, to_node, m.direction, nil, m.options, nil)
+ edge.event_index = m.event.index
+ edge.orig_m = m
+ graph.events[m.event.index] = { kind = 'edge', parameters = edge }
+ end
+ end
+ end
+ local da = g.syntactic_digraph:arc(a.head, a.tail)
+ if da then
+ for _,m in ipairs(da.syntactic_edges) do
+ if not mark[m] then
+ mark[m] = true
+ local from_node = graph:findNode(da.tail.name)
+ local to_node = graph:findNode(da.head.name)
+ local edge = graph:createEdge(from_node, to_node, m.direction, nil, m.options, nil)
+ edge.event_index = m.event.index
+ edge.orig_m = m
+ graph.events[m.event.index] = { kind = 'edge', parameters = edge }
+ end
+ end
+ end
+ end
+
+ table.sort(graph.edges, function(e1,e2) return e1.event_index < e2.event_index end)
+ for _,n in ipairs (graph.nodes) do
+ table.sort(n.edges, function(e1,e2) return e1.event_index < e2.event_index end)
+ end
+
+
+ -- Clusters
+ for _, c in ipairs(scope.collections['same layer'] or {}) do
+ cluster = Cluster.new("cluster" .. unique_count)
+ unique_count = unique_count+1
+ graph:addCluster(cluster)
+ for _,v in ipairs(c.vertices) do
+ if g:contains(v) then
+ cluster:addNode(graph:findNode(v.name))
+ end
+ end
+ end
+
+ return graph
+end
+
+
+local function compatibility_graph_to_digraph(graph)
+ for _,n in ipairs(graph.nodes) do
+ n.orig_vertex.pos.x = n.pos.x
+ n.orig_vertex.pos.y = n.pos.y
+ end
+ for _,e in ipairs(graph.edges) do
+ if #e.bend_points > 0 then
+ local c = {}
+ for _,x in ipairs(e.bend_points) do
+ c[#c+1] = Coordinate.new (x.x, x.y)
+ end
+ e.orig_m:setPolylinePath(c)
+ end
+ end
+end
+
+
+
+
+
+function LayoutPipeline.runOldGraphModel(scope, digraph, algorithm_class, algorithm)
+
+ local graph = compatibility_digraph_to_graph(scope, digraph)
+
+ algorithm.graph = graph
+ graph:registerAlgorithm(algorithm)
+
+ -- If requested, remove loops
+ if algorithm_class.preconditions.loop_free then
+ Simplifiers:removeLoopsOldModel(algorithm)
+ end
+
+ -- If requested, collapse multiedges
+ if algorithm_class.preconditions.simple then
+ Simplifiers:collapseMultiedgesOldModel(algorithm)
+ end
+
+ if #graph.nodes > 1 then
+ -- Main run of the algorithm:
+ algorithm:run ()
+ end
+
+ -- If requested, expand multiedges
+ if algorithm_class.preconditions.simple then
+ Simplifiers:expandMultiedgesOldModel(algorithm)
+ end
+
+ -- If requested, restore loops
+ if algorithm_class.preconditions.loop_free then
+ Simplifiers:restoreLoopsOldModel(algorithm)
+ end
+
+ compatibility_graph_to_digraph(graph)
+end
+
+
+
+
+-- Done
+
+return LayoutPipeline
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/control/NodeAnchors.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/control/NodeAnchors.lua
new file mode 100644
index 0000000000..80aa7ef420
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/control/NodeAnchors.lua
@@ -0,0 +1,175 @@
+-- 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$
+
+local declare = require "pgf.gd.interface.InterfaceToAlgorithms".declare
+
+
+
+---
+-- @section subsection {Anchoring Edges}
+--
+-- \label{section-gd-anchors}
+--
+-- When a graph has been laid out completely, the edges between the
+-- nodes must be drawn. Conceptually, an edge is ``between two
+-- nodes'', but when we actually draw the node, we do not really want
+-- the edge's path to start ``in the middle'' of the node; rather, we
+-- want it to start ``on the border'' and also end there.
+--
+-- Normally, computing such border positions for nodes is something we
+-- would leave to the so-called display layer (which is typically
+-- \tikzname\ and \tikzname\ is reasonably good at computing border
+-- positions). However, different display layers may behave
+-- differently here and even \tikzname\ fails when the node shapes are
+-- very involved and the paths also.
+--
+-- For these reasons, computing the anchor positions where edges start
+-- and end is done inside the graph drawing system. As a user, you
+-- specify a |tail anchor| and a |head anchor|, which are points
+-- inside the tail and head nodes, respectively. The edge path will
+-- then start and end at these points, however, they will usually be
+-- shortened so that they actually start and end on the intersection
+-- of the edge's path with the nodes' paths.
+
+
+---
+
+declare {
+ key = "tail anchor",
+ type = "string",
+ initial = "",
+
+ summary = [["
+ Specifies where in the tail vertex the edge should start.
+ "]],
+
+ documentation = [["
+ This is either a string or a number, interpreted as an angle
+ (with 90 meaning ``up''). If it is a string, when the start of
+ the edge is computed, we try to look up the anchor in the tail
+ vertex's table of anchors (some anchors get installed in this
+ table by the display system). If it is not found, we test
+ whether it is one of the special ``direction anchors'' like
+ |north| or |south east|. If so, we convert them into points on
+ the border of the node that lie in the direction of a line
+ starting at the center to a point on the bounding box of the
+ node in the designated direction. Finally, if the anchor is a
+ number, we use a point on the border of the node that is on a
+ line from the center in the specified direction.
+
+ If the anchor is set to the empty string (which is the default),
+ the anchor is interpreted as the |center| anchor inside the
+ graph drawing system. However, a display system may choose to
+ make a difference between the |center| anchor and an empty
+ anchor (\tikzname\ does: for options like |bend left| if the
+ anchor is empty, the bend line starts at the border of the node,
+ while for the anchor set explicitly to |center| it starts at the
+ center).
+
+ Note that graph drawing algorithms need not take the
+ setting of this option into consideration. However, the final
+ rendering of the edge will always take it into consideration
+ (only, the setting may not be very sensible if the algorithm
+ ignored it).
+ "]]
+}
+
+---
+
+declare {
+ key = "head anchor",
+ type = "string",
+ initial = "",
+
+ summary = "See |tail anchor|"
+}
+
+
+---
+
+declare {
+ key = "tail cut",
+ type = "boolean",
+ initial = true,
+
+ summary = [["
+ Decides whether the tail of an edge is ``cut'', meaning
+ that the edge's path will be shortened at the beginning so that
+ it starts only of the node's border rather than at the exact
+ position of the |tail anchor|, which may be inside the node.
+ "]]
+}
+
+
+---
+
+declare {
+ key = "head cut",
+ type = "boolean",
+ initial = true,
+
+ summary = "See |tail cut|."
+}
+
+
+---
+
+declare {
+ key = "cut policy",
+ type = "string",
+ initial = "as edge requests",
+
+ summary = "The policy for cutting edges entering or leaving a node.",
+
+ documentation = [["
+ This option is important for nodes only. It can have three
+ possible values:
+ %
+ \begin{itemize}
+ \item |as edge requests| Whether or not an edge entering or
+ leaving the node is cut depends on the setting of the edge's
+ |tail cut| and |head cut| options. This is the default.
+ \item |all| All edges entering or leaving the node are cut,
+ regardless of the edges' cut values.
+ \item |none| No edge entering or leaving the node is cut,
+ regardless of the edges' cut values.
+ \end{itemize}
+ "]]
+}
+
+
+---
+declare {
+ key = "allow inside edges",
+ type = "boolean",
+ initial = "true",
+
+ summary = "Decides whether an edge between overlapping nodes should be drawn.",
+
+ documentation = [["
+ If two vertices overlap, it may happen that when you draw an
+ edge between them, this edges would be completely inside the two
+ vertices. In this case, one could either not draw them or one
+ could draw a sort of ``inside edge''.
+ "]],
+
+ examples = { [["
+ \tikz \graph [no layout, nodes={draw, minimum size=20pt}] {
+ a [x=0, y=0] -- b [x=15pt, y=10pt] -- c[x=40pt]
+ };
+ "]],[["
+ \tikz \graph [no layout, nodes={draw, minimum size=20pt},
+ allow inside edges=false] {
+ a [x=0, y=0] -- b [x=15pt, y=10pt] -- c[x=40pt]
+ };
+ "]]
+ }
+} \ No newline at end of file
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/control/Orientation.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/control/Orientation.lua
new file mode 100644
index 0000000000..1890fa4f39
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/control/Orientation.lua
@@ -0,0 +1,303 @@
+-- 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$
+
+
+
+
+---
+-- @section subsection {Orienting a Graph}
+--
+-- \label{subsection-library-graphdrawing-standard-orientation}
+--
+-- Just as a graph drawing algorithm cannot know \emph{where} a graph
+-- should be placed on a page, it is also often unclear which
+-- \emph{orientation} it should have. Some graphs, like trees, have a
+-- natural direction in which they ``grow'', but for an ``arbitrary''
+-- graph the ``natural orientation'' is, well, arbitrary.
+--
+-- There are two ways in which you can specify an orientation: First,
+-- you can specify that the line from a certain vertex to another
+-- vertex should have a certain slope. How these vertices and slopes
+-- are specified in explained momentarily. Second, you can specify a
+-- so-called ``growth direction'' for trees.
+--
+-- @end
+
+
+-- Namespace
+require("pgf.gd.control").Orientation = Orientation
+
+
+
+-- Imports
+local declare = require "pgf.gd.interface.InterfaceToAlgorithms".declare
+
+
+---
+
+declare {
+ key = "orient",
+ type = "direction",
+ default = 0,
+
+ summary = [["
+ This key specifies that the straight line from the |orient tail| to
+ the |orient head| should be at an angle of \meta{direction} relative to
+ the right-going $x$-axis. Which vertices are used as tail an head
+ depends on where the |orient| option is encountered: When used with
+ an edge, the tail is the edge's tail and the head is the edge's
+ head. When used with a node, the tail or the head must be specified
+ explicitly and the node is used as the node missing in the
+ specification. When used with a graph as a whole, both the head and
+ tail nodes must be specified explicitly.
+ "]],
+ documentation = [["
+ Note that the \meta{direction} is independent of the actual to-path
+ of an edge, which might define a bend or more complicated shapes. For
+ instance, a \meta{angle} of |45| requests that the end node is ``up
+ and right'' relative to the start node.
+
+ You can also specify the standard direction texts |north| or |south east|
+ and so forth as \meta{direction} and also |up|, |down|, |left|, and
+ |right|. Also, you can specify |-| for ``right'' and \verb!|! for ``down''.
+ "]],
+ examples = {[["
+ \tikz \graph [spring layout]
+ {
+ a -- { b, c, d, e -- {f, g, h} };
+ h -- [orient=30] a;
+ };
+ "]],[["
+ \tikz \graph [spring layout]
+ {
+ a -- { b, c, d[> orient=right], e -- {f, g, h} };
+ h -- a;
+ };
+ "]]
+ }
+}
+
+
+---
+
+declare {
+ key = "orient'",
+ type = "direction",
+ default = 0,
+
+ summary = [["
+ Same as |orient|, only the rest of the graph should be
+ flipped relative to the connection line.
+ "]],
+ examples = [["
+ \tikz \graph [spring layout]
+ {
+ a -- { b, c, d[> orient'=right], e -- {f, g, h} };
+ h -- a;
+ };
+ "]]
+}
+
+---
+
+declare {
+ key = "orient tail",
+ type = "string",
+
+ summary = [["
+ Specifies the tail vertex for the orientation of a graph. See
+ |orient| for details.
+ "]],
+ examples = {[["
+ \tikz \graph [spring layout] {
+ a [orient=|, orient tail=f] -- { b, c, d, e -- {f, g, h} };
+ { h, g } -- a;
+ };
+ "]],[["
+ \tikz \graph [spring layout] {
+ a [orient=down, orient tail=h] -- { b, c, d, e -- {f, g, h} };
+ { h, g } -- a;
+ };
+ "]]
+ }
+}
+
+
+
+
+
+---
+
+declare {
+ key = "orient head",
+ type = "string",
+
+ summary = [["
+ Specifies the head vertex for the orientation of a graph. See
+ |orient| for details.
+ "]],
+ examples = {[["
+ \tikz \graph [spring layout]
+ {
+ a [orient=|, orient head=f] -- { b, c, d, e -- {f, g, h} };
+ { h, g } -- a;
+ };
+ "]],[["
+ \tikz \graph [spring layout] { a -- b -- c -- a };
+ \tikz \graph [spring layout, orient=10,
+ orient tail=a, orient head=b] { a -- b -- c -- a };
+ "]]
+ }
+}
+
+---
+
+declare {
+ key = "horizontal",
+ type = "string",
+
+ summary = [["
+ A shorthand for specifying |orient tail|, |orient head| and
+ |orient=0|. The tail will be everything before the part ``| to |''
+ and the head will be everything following it.
+ "]],
+ examples = [["
+ \tikz \graph [spring layout] { a -- b -- c -- a };
+ \tikz \graph [spring layout, horizontal=a to b] { a -- b -- c -- a };
+ "]]
+}
+
+
+
+
+---
+
+declare {
+ key = "horizontal'",
+ type = "string",
+
+ summary = [["
+ Like |horizontal|, but with a flip.
+ "]]
+}
+
+
+
+
+
+
+
+---
+
+declare {
+ key = "vertical",
+ type = "string",
+
+ summary = [["
+ A shorthand for specifying |orient tail|, |orient head| and |orient=-90|.
+ "]],
+ examples = [["
+ \tikz \graph [spring layout] { a -- b -- c -- a };
+ \tikz \graph [spring layout, vertical=a to b] { a -- b -- c -- a };
+ "]]
+}
+
+
+
+
+
+---
+
+declare {
+ key = "vertical'",
+ type = "string",
+
+ summary = [["
+ Like |vertical|, but with a flip.
+ "]]
+}
+
+
+
+---
+
+declare {
+ key = "grow",
+ type = "direction",
+
+ summary = [["
+ This key specifies in which direction the neighbors of a node
+ ``should grow''. For some graph drawing algorithms, especially for
+ those that layout trees, but also for those that produce layered
+ layouts, there is a natural direction in which the ``children'' of
+ a node should be placed. For instance, saying |grow=down| will cause
+ the children of a node in a tree to be placed in a left-to-right
+ line below the node (as always, you can replace the \meta{angle}
+ by direction texts). The children are requested to be placed in a
+ counter-clockwise fashion, the |grow'| key will place them in a
+ clockwise fashion.
+ "]],
+ documentation = [["
+ Note that when you say |grow=down|, it is not necessarily the case
+ that any particular node is actually directly below the current
+ node; the key just requests that the direction of growth is downward.
+
+ In principle, you can specify the direction of growth for each node
+ individually, but do not count on graph drawing algorithms to
+ honor these wishes.
+
+ When you give the |grow=right| key to the graph as a whole, it will
+ be applied to all nodes. This happens to be exactly what you want:
+ "]],
+ examples = {[["
+ \tikz \graph [layered layout, sibling distance=5mm]
+ {
+ a [grow=right] -- { b, c, d, e -- {f, g, h} };
+ { h, g } -- a;
+ };
+ "]],[["
+ \tikz \graph [layered layout, grow=right, sibling distance=5mm]
+ {
+ a -- { b, c, d, e -- {f, g, h} };
+ { h, g } -- a;
+ };
+ "]],[["
+ \tikz
+ \graph [layered layout, grow=-80]
+ {
+ {a,b,c} --[complete bipartite] {e,d,f}
+ --[complete bipartite] {g,h,i};
+ };
+ "]]
+ }
+}
+
+
+---
+
+declare {
+ key = "grow'",
+ type = "direction",
+
+ summary = "Same as |grow|, only with the children in clockwise order.",
+ examples = [["
+ \tikz \graph [layered layout, sibling distance=5mm]
+ {
+ a [grow'=right] -- { b, c, d, e -- {f, g, h} };
+ { h, g } -- a;
+ };
+ "]]
+}
+
+
+-- Done
+
+return Orientation \ No newline at end of file
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/control/Sublayouts.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/control/Sublayouts.lua
new file mode 100644
index 0000000000..970483193f
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/control/Sublayouts.lua
@@ -0,0 +1,536 @@
+-- Copyright 2012 by Till Tantau
+--
+-- This file may be distributed an/or modified
+--
+-- 1. under the LaTeX Project Public License and/or
+-- 2. under the GNU Public License
+--
+-- See the file doc/generic/pgf/licenses/LICENSE for more information
+
+-- @release $Header$
+
+local function full_print(g, pref)
+ local s = ""
+
+ for _,v in ipairs(g.vertices) do
+ s = s .. tostring(v) .. "[" .. tostring(v.pos) .. "]\n "
+ end
+
+ s = s .. "\n"
+
+ for _,a in ipairs(g.arcs) do
+ for _,e in ipairs(a.syntactic_edges) do
+ s = s .. tostring(e) .. "(" .. tostring(e.path) .. ")\n"
+ end
+ end
+
+ pgf.debug((pref or "") .. s)
+end
+
+
+---
+-- The |Sublayouts| module handles graphs for which multiple layouts are defined.
+--
+-- Please see Section~\ref{section-gd-sublayouts} for an overview of
+-- sublayouts.
+--
+
+local Sublayouts = {}
+
+-- Namespace
+require("pgf.gd.control").Sublayouts = Sublayouts
+
+
+-- Includes
+
+local Digraph = require "pgf.gd.model.Digraph"
+local Vertex = require "pgf.gd.model.Vertex"
+local Coordinate = require "pgf.gd.model.Coordinate"
+local Path = require "pgf.gd.model.Path"
+
+local lib = require "pgf.gd.lib"
+
+local InterfaceCore = require "pgf.gd.interface.InterfaceCore"
+
+local Storage = require "pgf.gd.lib.Storage"
+
+
+
+-- Storages
+
+local subs = Storage.newTableStorage()
+local already_nudged = Storage.new()
+local positions = Storage.newTableStorage()
+
+
+
+
+-- Offset a node by an offset. This will \emph{also} offset all
+-- subnodes, which arise from sublayouts.
+--
+-- @param vertex A vertex
+-- @param pos A offset
+--
+local function offset_vertex(v, delta)
+ v.pos:shiftByCoordinate(delta)
+ for _,sub in ipairs(subs[v]) do
+ offset_vertex(sub, delta)
+ end
+end
+
+
+-- Nudge positioning. You can call this function several times on the
+-- same graph; nudging will be done only once.
+--
+-- @param graph A graph
+--
+local function nudge(graph)
+ for _,v in ipairs(graph.vertices) do
+ local nudge = v.options['nudge']
+ if nudge and not already_nudged[v] then
+ offset_vertex(v, nudge)
+ already_nudged[v] = true
+ end
+ end
+end
+
+
+
+-- Create subgraph nodes
+--
+-- @param scope A scope
+-- @param syntactic_digraph The syntactic digraph.
+-- @param test Only for vertices whose subgraph collection passes this test will we create subgraph nodes
+local function create_subgraph_node(scope, syntactic_digraph, vertex)
+
+ local subgraph_collection = vertex.subgraph_collection
+ local binding = InterfaceCore.binding
+
+ local cloud = {}
+ -- Add all points of n's collection, except for v itself, to the cloud:
+ for _,v in ipairs(subgraph_collection.vertices) do
+ if vertex ~= v then
+ assert(syntactic_digraph:contains(v), "the layout must contain all nodes of the subgraph")
+ for _,p in ipairs(v.path) do
+ if type(p) == "table" then
+ cloud[#cloud+1] = p + v.pos
+ end
+ end
+ end
+ end
+ for _,e in ipairs(subgraph_collection.edges) do
+ for _,p in ipairs(e.path) do
+ if type(p) == "table" then
+ cloud[#cloud+1] = p:clone()
+ end
+ end
+ end
+ local x_min, y_min, x_max, y_max, c_x, c_y = Coordinate.boundingBox(cloud)
+
+ -- Shift the graph so that it is centered on the origin:
+ for _,p in ipairs(cloud) do
+ p:unshift(c_x,c_y)
+ end
+
+ local o = vertex.subgraph_info.generated_options
+
+ o[#o+1] = { key = "subgraph point cloud", value = table.concat(lib.imap(cloud, tostring)) }
+ o[#o+1] = { key = "subgraph bounding box height", value = tostring(y_max-y_min) .. "pt" }
+ o[#o+1] = { key = "subgraph bounding box width", value = tostring(x_max-x_min) .. "pt" }
+
+ -- And now, the "grand call":
+ binding:createVertex(vertex.subgraph_info)
+
+ -- Shift it were it belongs
+ vertex.pos:shift(c_x,c_y)
+
+ -- Remember all the subnodes for nudging and regardless
+ -- positioning
+ local s = {}
+ for _,v in ipairs(subgraph_collection.vertices) do
+ if v ~= vertex then
+ s[#s+1] = v
+ end
+ end
+
+ subs[vertex] = s
+end
+
+
+-- Tests whether two graphs have a vertex in common
+local function intersection(g1, g2)
+ for _,v in ipairs(g1.vertices) do
+ if g2:contains(v) then
+ return v
+ end
+ end
+end
+
+-- Tests whether a graph is a set is a subset of another
+local function special_vertex_subset(vertices, graph)
+ for _,v in ipairs(vertices) do
+ if not graph:contains(v) and not (v.kind == "subgraph node") then
+ return false
+ end
+ end
+ return true
+end
+
+
+
+---
+-- The layout recursion. See \ref{section-gd-sublayouts} for details.
+--
+-- @param scope The graph drawing scope
+-- @param layout The to-be-laid-out collection
+-- @param fun The to-be-called function for laying out the graph.
+-- processed. This stack is important when a new syntactic vertex is
+-- added by the algorithm: In this case, this vertex is added to all
+-- layouts on this stack.
+--
+-- @return A laid out graph.
+
+function Sublayouts.layoutRecursively(scope, layout, fun)
+
+ -- Step 1: Iterate over all sublayouts of the current layout:
+ local resulting_graphs = {}
+ local loc = Storage.new()
+
+ -- Now, iterate over all sublayouts
+ for i,child in ipairs(layout:childrenOfKind(InterfaceCore.sublayout_kind)) do
+ resulting_graphs[i] = Sublayouts.layoutRecursively(scope, child, fun)
+ loc[resulting_graphs[i]] = child
+ end
+
+ -- Step 2: Run the merge process:
+ local merged_graphs = {}
+
+ while #resulting_graphs > 0 do
+
+ local n = #resulting_graphs
+
+ -- Setup marked array:
+ local marked = {}
+ for i=1,n do
+ marked[i] = false
+ end
+
+ -- Mark first graph and copy everything from there
+ marked[1] = true
+ local touched = Storage.new()
+ for _,v in ipairs(resulting_graphs[1].vertices) do
+ v.pos = positions[v][resulting_graphs[1]]
+ touched[v] = true
+ end
+
+ -- Repeatedly find a node that is connected to a marked node:
+ local i = 1
+ while i <= n do
+ if not marked[i] then
+ for j=1,n do
+ if marked[j] then
+ local v = intersection(resulting_graphs[i], resulting_graphs[j])
+ if v then
+ -- Aha, they intersect at vertex v
+
+ -- Mark the i-th graph:
+ marked[i] = true
+ connected_some_graph = true
+
+ -- Shift the i-th graph:
+ local x_offset = v.pos.x - positions[v][resulting_graphs[i]].x
+ local y_offset = v.pos.y - positions[v][resulting_graphs[i]].y
+
+ for _,u in ipairs(resulting_graphs[i].vertices) do
+ if not touched[u] then
+ touched[u] = true
+ u.pos = positions[u][resulting_graphs[i]]:clone()
+ u.pos:shift(x_offset, y_offset)
+
+ for _,a in ipairs(resulting_graphs[i]:outgoing(u)) do
+ for _,e in ipairs(a.syntactic_edges) do
+ for _,p in ipairs(e.path) do
+ if type(p) == "table" then
+ p:shift(x_offset, y_offset)
+ end
+ end
+ end
+ end
+ end
+ end
+
+ -- Restart
+ i = 0
+ break
+ end
+ end
+ end
+ end
+ i = i + 1
+ end
+
+ -- Now, we can collapse all marked graphs into one graph:
+ local merge = Digraph.new {}
+ merge.syntactic_digraph = merge
+ local remaining = {}
+
+ -- Add all vertices and edges:
+ for i=1,n do
+ if marked[i] then
+ merge:add (resulting_graphs[i].vertices)
+ for _,a in ipairs(resulting_graphs[i].arcs) do
+ local ma = merge:connect(a.tail,a.head)
+ for _,e in ipairs(a.syntactic_edges) do
+ ma.syntactic_edges[#ma.syntactic_edges+1] = e
+ end
+ end
+ else
+ remaining[#remaining + 1] = resulting_graphs[i]
+ end
+ end
+
+ -- Remember the first layout this came from:
+ loc[merge] = loc[resulting_graphs[1]]
+
+ -- Restart with rest:
+ merged_graphs[#merged_graphs+1] = merge
+
+ resulting_graphs = remaining
+ end
+
+ -- Step 3: Run the algorithm on the layout:
+
+ local class = layout.options.algorithm_phases.main
+ assert (type(class) == "table", "algorithm selection failed")
+
+ local algorithm = class
+ local uncollapsed_subgraph_nodes = lib.imap(
+ scope.collections[InterfaceCore.subgraph_node_kind] or {},
+ function (c)
+ if c.parent_layout == layout then
+ return c.subgraph_node
+ end
+ end)
+
+
+ -- Create a new syntactic digraph:
+ local syntactic_digraph = Digraph.new {
+ options = layout.options
+ }
+
+ syntactic_digraph.syntactic_digraph = syntactic_digraph
+
+ -- Copy all vertices and edges from the collection...
+ syntactic_digraph:add (layout.vertices)
+ for _,e in ipairs(layout.edges) do
+ syntactic_digraph:add {e.head, e.tail}
+ local arc = syntactic_digraph:connect(e.tail, e.head)
+ arc.syntactic_edges[#arc.syntactic_edges+1] = e
+ end
+
+ -- Find out which subgraph nodes can be created now and make them part of the merged graphs
+ for i=#uncollapsed_subgraph_nodes,1,-1 do
+ local v = uncollapsed_subgraph_nodes[i]
+ local vertices = v.subgraph_collection.vertices
+ -- Test, if all vertices of the subgraph are in one of the merged graphs.
+ for _,g in ipairs(merged_graphs) do
+ if special_vertex_subset(vertices, g) then
+ -- Ok, we can create a subgraph now
+ create_subgraph_node(scope, syntactic_digraph, v)
+ -- Make it part of the collapse!
+ g:add{v}
+ -- Do not consider again
+ uncollapsed_subgraph_nodes[i] = false
+ break
+ end
+ end
+ end
+
+ -- Collapse the nodes that are part of a merged_graph
+ local collapsed_vertices = {}
+ for _,g in ipairs(merged_graphs) do
+
+ local intersection = {}
+ for _,v in ipairs(g.vertices) do
+ if syntactic_digraph:contains(v) then
+ intersection[#intersection+1] = v
+ end
+ end
+ if #intersection > 0 then
+ -- Compute bounding box of g (this should actually be the convex
+ -- hull) Hmm...:
+ local array = {}
+ for _,v in ipairs(g.vertices) do
+ local min_x, min_y, max_x, max_y = v:boundingBox()
+ array[#array+1] = Coordinate.new(min_x + v.pos.x, min_y + v.pos.y)
+ array[#array+1] = Coordinate.new(max_x + v.pos.x, max_y + v.pos.y)
+ end
+ for _,a in ipairs(g.arcs) do
+ for _,e in ipairs(a.syntactic_edges) do
+ for _,p in ipairs(e.path) do
+ if type(p) == "table" then
+ array[#array+1] = p
+ end
+ end
+ end
+ end
+ local x_min, y_min, x_max, y_max, c_x, c_y = Coordinate.boundingBox(array)
+
+ -- Shift the graph so that it is centered on the origin:
+ for _,v in ipairs(g.vertices) do
+ v.pos:unshift(c_x,c_y)
+ end
+ for _,a in ipairs(g.arcs) do
+ for _,e in ipairs(a.syntactic_edges) do
+ for _,p in ipairs(e.path) do
+ if type(p) == "table" then
+ p:unshift(c_x,c_y)
+ end
+ end
+ end
+ end
+
+ x_min = x_min - c_x
+ x_max = x_max - c_x
+ y_min = y_min - c_y
+ y_max = y_max - c_y
+
+ local index = loc[g].event.index
+
+ local v = Vertex.new {
+ -- Standard stuff
+ shape = "none",
+ kind = "node",
+ path = Path.new {
+ "moveto",
+ x_min, y_min,
+ x_min, y_max,
+ x_max, y_max,
+ x_max, y_min,
+ "closepath"
+ },
+ options = {},
+ event = scope.events[index]
+ }
+
+ -- Update node_event
+ scope.events[index].parameters = v
+
+ local collapse_vertex = syntactic_digraph:collapse(
+ intersection,
+ v,
+ nil,
+ function (new_arc, arc)
+ for _,e in ipairs(arc.syntactic_edges) do
+ new_arc.syntactic_edges[#new_arc.syntactic_edges+1] = e
+ end
+ end)
+
+ syntactic_digraph:remove(intersection)
+ collapsed_vertices[#collapsed_vertices+1] = collapse_vertex
+ end
+ end
+
+ -- Sort the vertices
+ table.sort(syntactic_digraph.vertices, function(u,v) return u.event.index < v.event.index end)
+
+ -- Should we "hide" the subgraph nodes?
+ local hidden_node
+ if not algorithm.include_subgraph_nodes then
+ local subgraph_nodes = lib.imap (syntactic_digraph.vertices,
+ function (v) if v.kind == "subgraph node" then return v end end)
+
+ if #subgraph_nodes > 0 then
+ hidden_node = Vertex.new {}
+ syntactic_digraph:collapse(subgraph_nodes, hidden_node)
+ syntactic_digraph:remove (subgraph_nodes)
+ syntactic_digraph:remove {hidden_node}
+ end
+ end
+
+ -- Now, we want to call the actual algorithm. This call may modify
+ -- the layout's vertices and edges fields, namely when new vertices
+ -- and edges are created. We then need to add these to our local
+ -- syntactic digraph. So, we remember the length of these fields
+ -- prior to the call and then add everything ``behind'' these
+ -- positions later on.
+
+ -- Ok, everything setup! Run the algorithm...
+ fun(scope, algorithm, syntactic_digraph, layout)
+
+ if hidden_node then
+ syntactic_digraph:expand(hidden_node)
+ end
+
+ -- Now, we need to expand the collapsed vertices once more:
+ for i=#collapsed_vertices,1,-1 do
+ syntactic_digraph:expand(
+ collapsed_vertices[i],
+ function (c, v)
+ v.pos:shiftByCoordinate(c.pos)
+ end,
+ function (a, v)
+ for _,e in ipairs(a.syntactic_edges) do
+ for _,p in ipairs(e.path) do
+ if type(p) == "table" then
+ p:shiftByCoordinate(v.pos)
+ end
+ end
+ end
+ end
+ )
+ for _,a in ipairs(syntactic_digraph:outgoing(collapsed_vertices[i])) do
+ for _,e in ipairs(a.syntactic_edges) do
+ for _,p in ipairs(e.path) do
+ if type(p) == "table" then
+ p:shiftByCoordinate(a.tail.pos)
+ p:unshiftByCoordinate(e.tail.pos)
+ end
+ end
+ end
+ end
+ end
+ syntactic_digraph:remove(collapsed_vertices)
+
+ -- Step 4: Create the layout node if necessary
+ for i=#uncollapsed_subgraph_nodes,1,-1 do
+ if uncollapsed_subgraph_nodes[i] then
+ create_subgraph_node(scope, syntactic_digraph, uncollapsed_subgraph_nodes[i])
+ end
+ end
+
+ -- Now seems like a good time to nudge and do regardless positioning
+ nudge(syntactic_digraph)
+
+ -- Step 5: Cleanup
+ -- Push the computed position into the storage:
+ for _,v in ipairs(syntactic_digraph.vertices) do
+ positions[v][syntactic_digraph] = v.pos:clone()
+ end
+
+ return syntactic_digraph
+end
+
+
+
+
+
+---
+-- Regardless positioning.
+--
+-- @param graph A graph
+--
+function Sublayouts.regardless(graph)
+ for _,v in ipairs(graph.vertices) do
+ local regardless = v.options['regardless at']
+ if regardless then
+ offset_vertex(v, regardless - v.pos)
+ end
+ end
+end
+
+
+
+-- Done
+
+return Sublayouts
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/control/doc.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/control/doc.lua
new file mode 100644
index 0000000000..19bfd52fdf
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/control/doc.lua
@@ -0,0 +1,245 @@
+-- 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$
+
+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 "desired at"
+
+summary
+[[
+When you add this key to a node in a graph, you ``desire''
+that the node should be placed at the \meta{coordinate} by the graph
+drawing algorithm.
+]]
+
+documentation
+[[
+Now, when you set this key for a single node of a graph,
+then, by shifting the graph around, this ``wish'' can obviously
+always be fulfill:
+%
+\begin{codeexample}[preamble={ \usetikzlibrary{graphs,graphdrawing}
+ \usegdlibrary{force}}]
+\begin{tikzpicture}
+ \draw [help lines] (0,0) grid (3,2);
+ \graph [spring layout]
+ {
+ a [desired at={(1,2)}] -- b -- c -- a;
+ };
+\end{tikzpicture}
+\end{codeexample}
+%
+\begin{codeexample}[preamble={ \usetikzlibrary{graphs,graphdrawing}
+ \usegdlibrary{force}}]
+\begin{tikzpicture}
+ \draw [help lines] (0,0) grid (3,2);
+ \graph [spring layout]
+ {
+ a -- b[desired at={(2,1)}] -- c -- a;
+ };
+\end{tikzpicture}
+\end{codeexample}
+%
+Since the key's name is a bit long and since the many braces and
+parentheses are a bit cumbersome, there is a special support for
+this key inside a |graph|: The standard |/tikz/at| key is redefined
+inside a |graph| so that it points to |/graph drawing/desired at|
+instead. (Which is more logical anyway, since it makes no sense to
+specify an |at| position for a node whose position it to be computed
+by a graph drawing algorithm.) A nice side effect of this is that
+you can use the |x| and |y| keys (see
+Section~\ref{section-graphs-xy}) to specify desired positions:
+%
+\begin{codeexample}[preamble={ \usetikzlibrary{graphs,graphdrawing}
+ \usegdlibrary{force}}]
+\begin{tikzpicture}
+ \draw [help lines] (0,0) grid (3,2);
+ \graph [spring layout]
+ {
+ a -- b[x=2,y=0] -- c -- a;
+ };
+\end{tikzpicture}
+\end{codeexample}
+%
+\begin{codeexample}[preamble={ \usetikzlibrary{graphs,graphdrawing}
+ \usegdlibrary{layered}}]
+\begin{tikzpicture}
+ \draw [help lines] (0,0) grid (3,2);
+ \graph [layered layout]
+ {
+ a [x=1,y=2] -- { b, c } -- {e, f} -- a
+ };
+\end{tikzpicture}
+\end{codeexample}
+
+A problem arises when two or more nodes have this key set, because
+then your ``desires'' for placement and the positions computed by
+the graph drawing algorithm may clash. Graph drawing algorithms are
+``told'' about the desired positions. Most algorithms will simply
+ignore these desired positions since they will be taken care of in
+the so-called post-anchoring phase, see below. However, for some
+algorithms it makes a lot of sense to fix the positions of some
+nodes and only compute the positions of the other nodes relative
+to these nodes. For instance, for a |spring layout| it makes perfect
+sense that some nodes are ``nailed to the canvas'' while other
+nodes can ``move freely''.
+]]
+
+--[[
+% TODOsp: codeexamples: the following 3 examples need these libraries
+% \usetikzlibrary{graphs,graphdrawing}
+% \usegdlibrary{force}
+--]]
+example
+[[
+\begin{tikzpicture}
+ \draw [help lines] (0,0) grid (3,2);
+ \graph [spring layout]
+ {
+ a[x=1] -- { b, c, d, e -- {f,g,h} };
+ { h, g } -- a;
+ };
+\end{tikzpicture}
+]]
+
+example
+[[
+\begin{tikzpicture}
+ \draw [help lines] (0,0) grid (3,2);
+ \graph [spring layout]
+ {
+ a -- { b, c, d[x=0], e -- {f[x=2], g, h[x=1]} };
+ { h, g } -- a;
+ };
+\end{tikzpicture}
+]]
+
+example
+[[
+\begin{tikzpicture}
+ \draw [help lines] (0,0) grid (3,2);
+ \graph [spring layout]
+ {
+ a -- { b, c, d[x=0], e -- {f[x=2,y=1], g, h[x=1]} };
+ { h, g } -- a;
+ };
+\end{tikzpicture}
+]]
+--------------------------------------------------------------------
+
+
+
+--------------------------------------------------------------------
+key "anchor node"
+
+summary
+[[
+This option can be used with a graph to specify a node that
+should be used for anchoring the whole graph.
+]]
+
+documentation
+[[
+When this option is specified, after the layout has been computed, the
+whole graph will be shifted in such a way that the \meta{node name} is
+either
+%
+\begin{itemize}
+ \item at the current value of |anchor at| or
+ \item at the position that is specified in the form of a
+ |desired at| for the \meta{node name}.
+\end{itemize}
+%
+Note how in the last example |c| is placed at |(1,1)| rather than
+|b| as would happen by default.
+]]
+
+--[[
+% TODOsp: codeexamples: the following 4 examples need these libraries
+% \usetikzlibrary{graphs,graphdrawing}
+% \usegdlibrary{layered}
+--]]
+example
+[[
+\tikz \draw (0,0)
+ -- (1,0.5) graph [edges=red, layered layout, anchor node=a] { a -> {b,c} }
+ -- (1.5,0) graph [edges=blue, layered layout,
+ anchor node=y, anchor at={(2,0)}] { x -> {y,z} };
+]]
+
+example
+[[
+\begin{tikzpicture}
+ \draw [help lines] (0,0) grid (3,2);
+
+ \graph [layered layout, anchor node=c, edges=rounded corners]
+ { a -- {b [x=1,y=1], c [x=1,y=1] } -- d -- a};
+\end{tikzpicture}
+]]
+--------------------------------------------------------------------
+
+
+
+
+--------------------------------------------------------------------
+key "anchor at"
+
+summary
+[[
+The coordinate at which the graph should be anchored when no
+explicit anchor is given for any node. The initial value is the origin.
+]]
+
+example
+[[
+\begin{tikzpicture}
+ \draw [help lines] (0,0) grid (2,2);
+
+ \graph [layered layout, edges=rounded corners, anchor at={(1,2)}]
+ { a -- {b, c [anchor here] } -- d -- a};
+\end{tikzpicture}
+]]
+--------------------------------------------------------------------
+
+
+
+
+--------------------------------------------------------------------
+key "anchor here"
+
+summary
+[[
+This option can be passed to a single node (rather than the
+graph as a whole) in order to specify that this node should be used
+for the anchoring process.
+]]
+
+documentation
+[[
+In the example, |c| is placed at the origin since this is the
+default |anchor at| position.
+]]
+
+example
+[[
+\begin{tikzpicture}
+ \draw [help lines] (0,0) grid (2,2);
+
+ \graph [layered layout, edges=rounded corners]
+ { a -- {b, c [anchor here] } -- d -- a};
+\end{tikzpicture}
+]]
+--------------------------------------------------------------------
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/control/library.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/control/library.lua
new file mode 100644
index 0000000000..df7ce68dd9
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/control/library.lua
@@ -0,0 +1,196 @@
+-- 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$
+
+
+-- Load declarations from:
+
+require "pgf.gd.control.FineTune"
+require "pgf.gd.control.Anchoring"
+require "pgf.gd.control.Sublayouts"
+require "pgf.gd.control.Orientation"
+require "pgf.gd.control.Distances"
+require "pgf.gd.control.Components"
+require "pgf.gd.control.ComponentAlign"
+require "pgf.gd.control.ComponentDirection"
+require "pgf.gd.control.ComponentDistance"
+require "pgf.gd.control.ComponentOrder"
+require "pgf.gd.control.NodeAnchors"
+
+
+local InterfaceCore = require "pgf.gd.interface.InterfaceCore"
+local declare = require "pgf.gd.interface.InterfaceToAlgorithms".declare
+local lib = require "pgf.gd.lib"
+
+
+
+---
+
+declare {
+ key = "nodes behind edges",
+ type = "boolean",
+
+ summary = "Specifies, that nodes should be drawn behind the edges",
+ documentation = [["
+ Once a graph drawing algorithm has determined positions for the nodes,
+ they are drawn \emph{before} the edges are drawn; after
+ all, it is hard to draw an edge between nodes when their positions
+ are not yet known. However, we typically want the nodes to be
+ rendered \emph{after} or rather \emph{on top} of the edges. For
+ this reason, the default behavior is that the nodes at their
+ final positions are collected in a box that is inserted into the
+ output stream only after the edges have been drawn -- which has
+ the effect that the nodes will be placed ``on top'' of the
+ edges.
+
+ This behavior can be changed using this option. When the key is
+ invoked, nodes are placed \emph{behind} the edges.
+ "]],
+ examples = [["
+ \tikz \graph [simple necklace layout, nodes={draw,fill=white},
+ nodes behind edges]
+ { subgraph K_n [n=7], 1 [regardless at={(0,-1)}] };
+ "]]
+}
+
+
+---
+
+declare {
+ key = "edges behind nodes",
+ use = {
+ { key = "nodes behind edges", value = "false" },
+ },
+
+ summary = [["
+ This is the default placement of edges: Behind the nodes.
+ "]],
+ examples = [["
+ \tikz \graph [simple necklace layout, nodes={draw,fill=white},
+ edges behind nodes]
+ { subgraph K_n [n=7], 1 [regardless at={(0,-1)}] };
+ "]]
+}
+
+---
+declare {
+ key = "random seed",
+ type = "number",
+ initial = "42",
+
+ summary = [["
+ To ensure that the same is always shown in the same way when the
+ same algorithm is applied, the random is seed is reset on each call
+ of the graph drawing engine. To (possibly) get different results on
+ different runs, change this value.
+ "]]
+}
+
+
+---
+declare {
+ key = "variation",
+ type = "number",
+ use = {
+ { key = "random seed", value = lib.id },
+ },
+ summary = "An alias for |random seed|."
+}
+
+
+---
+declare {
+ key = "weight",
+ type = "number",
+ initial = 1,
+
+ summary = [["
+ Sets the ``weight'' of an edge or a node. For many algorithms, this
+ number tells the algorithm how ``important'' the edge or node is.
+ For instance, in a |layered layout|, an edge with a large |weight|
+ will be as short as possible.
+ "]],
+ examples = {[["
+ \tikz \graph [layered layout] {
+ a -- {b,c,d} -- e -- a;
+ };
+ "]],[["
+ \tikz \graph [layered layout] {
+ a -- {b,c,d} -- e --[weight=3] a;
+ };
+ "]]
+ }
+}
+
+
+
+---
+declare {
+ key = "length",
+ type = "length",
+ initial = 1,
+
+ summary = [["
+ Sets the ``length'' of an edge. Algorithms may take this value
+ into account when drawing a graph.
+ "]],
+ examples = {[["
+ \tikz \graph [phylogenetic tree layout] {
+ a --[length=2] b --[length=1] {c,d};
+ a --[length=3] e
+ };
+ "]],
+ }
+}
+
+
+---
+
+declare {
+ key = "radius",
+ type = "number",
+ initial = "0",
+
+ summary = [["
+ The radius of a circular object used in graph drawing.
+ "]]
+}
+
+---
+
+declare {
+ key = "no layout",
+ algorithm = {
+ run =
+ function (self)
+ for _,v in ipairs(self.digraph.vertices) do
+ if v.options['desired at'] then
+ v.pos.x = v.options['desired at'].x
+ v.pos.y = v.options['desired at'].y
+ end
+ end
+ end },
+ summary = "This layout does nothing.",
+}
+
+
+
+-- The following collection kinds are internal
+
+declare {
+ key = InterfaceCore.sublayout_kind,
+ layer = 0
+}
+
+declare {
+ key = InterfaceCore.subgraph_node_kind,
+ layer = 0
+}
+
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/deprecated/Cluster.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/deprecated/Cluster.lua
new file mode 100644
index 0000000000..d7baed45da
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/deprecated/Cluster.lua
@@ -0,0 +1,65 @@
+-- Copyright 2011 by Jannis Pohlmann
+-- 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$
+
+
+
+--- The Cluster class defines a model of a cluster inside a graph.
+--
+--
+
+local Cluster = {}
+Cluster.__index = Cluster
+
+
+-- Namespace
+
+
+
+--- TODO Jannis: Add documentation for this class.
+--
+function Cluster.new(name)
+ local cluster = {
+ name = name,
+ nodes = {},
+ contains_node = {},
+ }
+ setmetatable(cluster, Cluster)
+ return cluster
+end
+
+
+
+function Cluster:getName()
+ return self.name
+end
+
+
+
+function Cluster:addNode(node)
+ if not self:findNode(node) then
+ self.contains_node[node] = true
+ self.nodes[#self.nodes + 1] = node
+ end
+end
+
+
+
+function Cluster:findNode(node)
+ return self.contains_node[node]
+end
+
+
+
+
+-- Done
+
+return Cluster \ No newline at end of file
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/deprecated/Edge.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/deprecated/Edge.lua
new file mode 100644
index 0000000000..7355597955
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/deprecated/Edge.lua
@@ -0,0 +1,358 @@
+-- Copyright 2010 by Renée Ahrens, Olof Frahm, Jens Kluttig, Matthias Schulz, Stephan Schuster
+-- Copyright 2011 by Jannis Pohlmann
+-- 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$
+
+
+
+
+--- The Edge class
+--
+--
+
+local Edge = {}
+Edge.__index = Edge
+
+
+-- Namespace
+
+local lib = require "pgf.gd.lib"
+
+
+-- Definitions
+
+Edge.UNDIRECTED = "--"
+Edge.LEFT = "<-"
+Edge.RIGHT = "->"
+Edge.BOTH = "<->"
+Edge.NONE = "-!-"
+
+
+--- Creates an edge between nodes of a graph.
+--
+-- @param values Values to override default edge settings.
+-- The following parameters can be set:\par
+-- |nodes|: TODO \par
+-- |edge_nodes|: TODO \par
+-- |options|: TODO \par
+-- |tikz_options|: TODO \par
+-- |direction|: TODO \par
+-- |bend_points|: TODO \par
+-- |bend_nodes|: TODO \par
+-- |reversed|: TODO \par
+--
+-- @return A newly-allocated edge.
+--
+function Edge.new(values)
+ local defaults = {
+ nodes = {},
+ edge_nodes = '',
+ options = {},
+ tikz_options = {},
+ direction = Edge.DIRECTED,
+ bend_points = {},
+ bend_nodes = {},
+ reversed = false,
+ algorithmically_generated_options = {},
+ index = nil,
+ event_index = nil,
+ }
+ setmetatable(defaults, Edge)
+ if values then
+ for k,v in pairs(values) do
+ defaults[k] = v
+ end
+ end
+ return defaults
+end
+
+
+
+--- Sets the edge option \meta{name} to \meta{value}.
+--
+-- @param name Name of the option to be changed.
+-- @param value New value for the edge option \meta{name}.
+--
+function Edge:setOption(name, value)
+ self.options[name] = value
+end
+
+
+
+--- Returns the value of the edge option \meta{name}.
+--
+-- @param name Name of the option.
+-- @param graph If this optional argument is given,
+-- in case the option is not set as a node parameter,
+-- we try to look it up as a graph parameter.
+--
+-- @return The value of the edge option \meta{name} or |nil|.
+--
+function Edge:getOption(name, graph)
+ return lib.lookup_option(name, self, graph)
+end
+
+
+
+--- Checks whether or not the edge is a loop edge.
+--
+-- An edge is a loop if it one node multiple times and no other node.
+--
+-- @return |true| if the edge is a loop, |false| otherwise.
+--
+function Edge:isLoop()
+ local nodes = self.nodes
+ for i=1,#nodes do
+ if nodes[i] ~= nodes[1] then
+ return false
+ end
+ end
+ return true
+end
+
+
+
+--- Returns whether or not the edge is a hyperedge.
+--
+-- A hyperedge is an edge with more than two adjacent nodes.
+--
+-- @return |true| if the edge is a hyperedge. |false| otherwise.
+--
+function Edge:isHyperedge()
+ return self:getDegree() > 2
+end
+
+
+
+--- Returns all nodes of the edge.
+--
+-- Instead of calling |edge:getNodes()| the nodes can alternatively be
+-- accessed directly with |edge.nodes|.
+--
+-- @return All edges of the node.
+--
+function Edge:getNodes()
+ return self.nodes
+end
+
+
+
+--- Returns whether or not a node is adjacent to the edge.
+--
+-- @param node The node to check.
+--
+-- @return |true| if the node is adjacent to the edge. |false| otherwise.
+--
+function Edge:containsNode(node)
+ return lib.find(self.nodes, function (other) return other == node end) ~= nil
+end
+
+
+
+--- If possible, adds a node to the edge.
+--
+-- @param node The node to be added to the edge.
+--
+function Edge:addNode(node)
+ table.insert(self.nodes, node)
+ node:addEdge(self)
+end
+
+
+
+--- Gets first neighbor of the node (disregarding hyperedges).
+--
+-- @param node The node which first neighbor should be returned.
+--
+-- @return The first neighbor of the node.
+--
+function Edge:getNeighbour(node)
+ if node == self.nodes[1] then
+ return self.nodes[#self.nodes]
+ else
+ return self.nodes[1]
+ end
+end
+
+
+
+--- Counts the nodes on this edge.
+--
+-- @return The number of nodes on the edge.
+--
+function Edge:getDegree()
+ return #self.nodes
+end
+
+
+
+function Edge:getHead()
+ -- by default, the head of -> edges is the last node and the head
+ -- of <- edges is the first node
+ local head_index = (self.direction == Edge.LEFT) and 1 or #self.nodes
+
+ -- if the edge should be assumed reversed, we simply switch head and
+ -- tail positions
+ if self.reversed then
+ head_index = (head_index == 1) and #self.nodes or 1
+ end
+
+ return self.nodes[head_index]
+end
+
+
+
+function Edge:getTail()
+ -- by default, the tail of -> edges is the first node and the tail
+ -- of <- edges is the last node
+ local tail_index = (self.direction == Edge.LEFT) and #self.nodes or 1
+
+ -- if the edge should be assumed reversed, we simply switch head
+ -- and tail positions
+ if self.reversed then
+ tail_index = (tail_index == 1) and #self.nodes or 1
+ end
+
+ return self.nodes[tail_index]
+end
+
+
+
+--- Checks whether a node is the head of the edge. Does not work for hyperedges.
+--
+-- This method only works for edges with two adjacent nodes.
+--
+-- Edges may be reversed internally, so their head and tail might be switched.
+-- Whether or not this internal reversal is handled by this method
+-- can be specified with the optional second \meta{ignore\_reversed} parameter
+-- which is |false| by default.
+--
+-- @param node The node to check.
+--
+-- @return True if the node is the head of the edge.
+--
+function Edge:isHead(node)
+ local result = false
+
+ -- by default, the head of -> edges is the last node and the head
+ -- of <- edges is the first node
+ local head_index = (self.direction == Edge.LEFT) and 1 or #self.nodes
+
+ -- if the edge should be assumed reversed, we simply switch head and
+ -- tail positions
+ if self.reversed then
+ head_index = (head_index == 1) and #self.nodes or 1
+ end
+
+ -- check if the head node equals the input node
+ if self.nodes[head_index].name == node.name then
+ result = true
+ end
+
+ return result
+end
+
+
+
+--- Checks whether a node is the tail of the edge. Does not work for hyperedges.
+--
+-- This method only works for edges with two adjacent nodes.
+--
+-- Edges may be reversed internally, so their head and tail might be switched.
+-- Whether or not this internal reversal is handled by this method
+-- can be specified with the optional second \meta{ignore\_reversed} parameter
+-- which is |false| by default.
+--
+-- @param node The node to check.
+-- @param ignore_reversed Optional parameter. Set this to true if reversed edges
+-- should not be considered reversed for this method call.
+--
+-- @return True if the node is the tail of the edge.
+--
+function Edge:isTail(node, ignore_reversed)
+ local result = false
+
+ -- by default, the tail of -> edges is the first node and the tail
+ -- of <- edges is the last node
+ local tail_index = (self.direction == Edge.LEFT) and #self.nodes or 1
+
+ -- if the edge should be assumed reversed, we simply switch head
+ -- and tail positions
+ if self.reversed then
+ tail_index = (tail_index == 1) and #self.nodes or 1
+ end
+
+ -- check if the tail node equals the input node
+ if self.nodes[tail_index].name == node.name then
+ result = true
+ end
+
+ return result
+end
+
+
+
+--- Copies an edge (preventing accidental use).
+--
+-- The nodes of the edge are not preserved and have to be added
+-- to the copy manually if necessary.
+--
+-- @return Shallow copy of the edge.
+--
+function Edge:copy()
+ local result = lib.copy(self, Edge.new())
+ result.nodes = {}
+ return result
+ end
+
+
+
+
+local function reverse_values(source)
+ local copy = {}
+ for i = 1,#source do
+ copy[i] = source[#source-i+1]
+ end
+ return copy
+end
+
+
+--- Returns a readable string representation of the edge.
+--
+-- @ignore This should not appear in the documentation.
+--
+-- @return String representation of the edge.
+--
+function Edge:__tostring()
+ local result = "Edge(" .. self.direction .. ", reversed = " .. tostring(self.reversed) .. ", "
+ if #self.nodes > 0 then
+ local node_strings = lib.imap(self.nodes, function (node) return node.name end)
+ result = result .. table.concat(node_strings, ', ')
+ end
+ --return result .. ")"
+
+ -- Note: the following lines generate a shorter string representation
+ -- of the edge that is more readable and can be used for debugging.
+ -- So please don't remove this:
+ --
+ local node_strings = lib.imap(self.nodes, function (node) return node.name end)
+ if self.reversed then
+ return table.concat(reverse_values(node_strings), ' ' .. self.direction .. ' ')
+ else
+ return table.concat(node_strings, ' ' .. self.direction .. ' ')
+ end
+end
+
+
+
+-- Done
+
+return Edge \ No newline at end of file
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/deprecated/Graph.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/deprecated/Graph.lua
new file mode 100644
index 0000000000..161b89fd15
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/deprecated/Graph.lua
@@ -0,0 +1,396 @@
+-- Copyright 2010 by Renée Ahrens, Olof Frahm, Jens Kluttig, Matthias Schulz, Stephan Schuster
+-- Copyright 2011 by Jannis Pohlmann
+-- 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$
+
+
+
+
+--- The Graph class
+--
+--
+
+local Graph = {}
+Graph.__index = Graph
+
+
+-- Namespace
+
+-- Imports
+local Edge = require "pgf.gd.deprecated.Edge"
+
+local lib = require "pgf.gd.lib"
+
+
+--- Creates a new graph.
+--
+-- @param values Values to override default graph settings.
+-- The following parameters can be set:\par
+-- |nodes|: The nodes of the graph.\par
+-- |edges|: The edges of the graph.\par
+-- |clusters|: The node clusters of the graph.\par
+-- |options|: A table of node options passed over from \tikzname.
+-- |events|: A sequence of events signaled during the graph specification.
+--
+-- @return A newly-allocated graph.
+--
+function Graph.new(values)
+ local defaults = {
+ nodes = {},
+ edges = {},
+ clusters = {},
+ options = {},
+ events = {},
+ }
+ setmetatable(defaults, Graph)
+ if values then
+ for k,v in pairs(values) do
+ defaults[k] = v
+ end
+ end
+ return defaults
+end
+
+
+
+--- Prepares a graph for an algorithm.
+--
+-- This method causes self, all its nodes, and all its edges to get
+-- a new empty table for the key algorithm. This allows an algorithm to
+-- store stuff with nodes and edges without them interfering with information
+-- stored by other algorithms.
+--
+-- @param An algorithm object.
+
+function Graph:registerAlgorithm(algorithm)
+ self[algorithm] = self[algorithm] or {}
+
+ -- Add an algorithm field to all nodes, all edges, and the graph:
+ for _,n in pairs(self.nodes) do
+ n[algorithm] = n[algorithm] or {}
+ end
+ for _,e in pairs(self.edges) do
+ e[algorithm] = e[algorithm] or {}
+ end
+end
+
+
+--- Sets the graph option \meta{name} to \meta{value}.
+--
+-- @param name Name of the option to be changed.
+-- @param value New value for the graph option \meta{name}.
+--
+function Graph:setOption(name, value)
+ self.options[name] = value
+end
+
+
+
+--- Returns the value of the graph option \meta{name}.
+--
+-- @param name Name of the option.
+--
+-- @return The value of the graph option \meta{name} or |nil|.
+--
+function Graph:getOption(name)
+ return self.options[name]
+end
+
+
+
+
+--- Creates a shallow copy of a graph.
+--
+-- The nodes and edges of the original graph are not preserved in the copy.
+--
+-- @return A shallow copy of the graph.
+--
+function Graph:copy ()
+ return Graph.new({options = self.options, events = self.events})
+end
+
+
+--- Adds a node to the graph.
+--
+-- @param node The node to be added.
+--
+function Graph:addNode(node)
+ -- only add the node if it's not included in the graph yet
+ if not self:findNode(node.name) then
+ -- Does the node have an index, yet?
+ if not node.index then
+ node.index = #self.nodes + 1
+ end
+
+ table.insert(self.nodes, node)
+ end
+end
+
+
+
+--- If possible, removes a node from the graph and returns it.
+--
+-- @param node The node to remove.
+--
+-- @return The removed node or |nil| if it was not found in the graph.
+--
+function Graph:removeNode(node)
+ local _, index = lib.find(self.nodes, function (other)
+ return other.name == node.name
+ end)
+ if index then
+ table.remove(self.nodes, index)
+ return node
+ else
+ return nil
+ end
+end
+
+
+
+--- If possible, looks up the node with the given name in the graph.
+--
+-- @param name Name of the node to look up.
+--
+-- @return The node with the given name or |nil| if it was not found in the graph.
+--
+function Graph:findNode(name)
+ return self:findNodeIf(function (node) return node.name == name end)
+end
+
+
+
+--- Looks up the first node for which the function \meta{test} returns |true|.
+--
+-- @param test A function that takes one parameter (a |Node|) and returns
+-- |true| or |false|.
+--
+-- @return The first node for which \meta{test} returns |true|.
+--
+function Graph:findNodeIf(test)
+ return lib.find(self.nodes, test)
+end
+
+
+
+--- Like removeNode, but also deletes all adjacent edges of the removed node.
+--
+-- This function also removes the deleted adjacent edges from all neighbors
+-- of the removed node.
+--
+-- @param node The node to be deleted together with its adjacent edges.
+--
+-- @return The removed node or |nil| if the node was not found in the graph.
+--
+function Graph:deleteNode(node)
+ local node = self:removeNode(node)
+ if node then
+ for _,edge in ipairs(node.edges) do
+ self:removeEdge(edge)
+ for _,other_node in ipairs(edge.nodes) do
+ if other_node.name ~= node.name then
+ other_node:removeEdge(edge)
+ end
+ end
+ end
+ node.edges = {}
+ end
+ return node
+end
+
+
+
+-- Checks whether the edge already exists in the graph and returns it if possible.
+--
+-- @param edge Edge to search for.
+--
+-- @return The edge if it was found in the graph, |nil| otherwise.
+--
+function Graph:findEdge(edge)
+ return lib.find(self.edges, function (other) return other == edge end)
+end
+
+
+
+--- Adds an edge to the graph.
+--
+-- @param edge The edge to be added.
+--
+function Graph:addEdge(edge)
+ if not edge.index then
+ edge.index = #self.edges + 1
+ end
+
+ table.insert(self.edges, edge)
+end
+
+
+
+--- If possible, removes an edge from the graph and returns it.
+--
+-- @param edge The edge to be removed.
+--
+-- @return The removed edge or |nil| if it was not found in the graph.
+--
+function Graph:removeEdge(edge)
+ local _, index = lib.find(self.edges, function (other) return other == edge end)
+ if index then
+ table.remove(self.edges, index)
+ return edge
+ else
+ return nil
+ end
+end
+
+
+
+--- Like removeEdge, but also removes the edge from its adjacent nodes.
+--
+-- @param edge The edge to be deleted.
+--
+-- @return The removed edge or |nil| if it was not found in the graph.
+--
+function Graph:deleteEdge(edge)
+ local edge = self:removeEdge(edge)
+ if edge then
+ for _,node in ipairs(edge.nodes) do
+ node:removeEdge(edge)
+ end
+ end
+ return edge
+end
+
+
+
+--- Removes an edge between two nodes and also removes it from these nodes.
+--
+-- @param from Start node of the edge.
+-- @param to End node of the edge.
+--
+-- @return The deleted edge.
+--
+function Graph:deleteEdgeBetweenNodes(from, to)
+ -- try to find the edge
+ local edge = lib.find(self.edges, function (edge)
+ return edge.nodes[1] == from and edge.nodes[2] == to
+ end)
+
+ -- delete and return the edge
+ if edge then
+ return self:deleteEdge(edge)
+ else
+ return nil
+ end
+end
+
+
+
+--- Creates and adds a new edge to the graph.
+--
+-- @param first_node The first node of the new edge.
+-- @param second_node The second node of the new edge.
+-- @param direction The direction of the new edge. Possible values are
+-- \begin{itemize}
+-- \item |Edge.UNDIRECTED|,
+-- \item |Edge.LEFT|,
+-- \item |Edge.RIGHT|,
+-- \item |Edge.BOTH| and
+-- \item |Edge.NONE| (for invisible edges).
+-- \end{itemize}
+-- @param edge_nodes A string of \tikzname\ edge nodes that needs to be passed
+-- back to the \TeX layer unmodified.
+-- @param options The options of the new edge.
+-- @param tikz_options A table of \tikzname\ options to be used by graph drawing
+-- algorithms to treat the edge in special ways.
+--
+-- @return The newly created edge.
+--
+function Graph:createEdge(first_node, second_node, direction, edge_nodes, options, tikz_options)
+ local edge = Edge.new{
+ direction = direction,
+ edge_nodes = edge_nodes,
+ options = options,
+ tikz_options = tikz_options
+ }
+ edge:addNode(first_node)
+ edge:addNode(second_node)
+ self:addEdge(edge)
+ return edge
+end
+
+
+
+--- Returns the cluster with the given name or |nil| if no such cluster exists.
+--
+-- @param name Name of the node cluster to look up.
+--
+-- @return The cluster with the given name or |nil| if no such cluster is defined.
+--
+function Graph:findClusterByName(name)
+ return lib.find(self.clusters, function (cluster)
+ return cluster.name == name
+ end)
+end
+
+
+
+--- Tries to add a cluster to the graph. Returns whether or not this was successful.
+--
+-- Clusters are supposed to have unique names. This function will add the given
+-- cluster only if there is no cluster with this name already. It returns |true|
+-- if the cluster was added and |false| otherwise.
+--
+-- @param cluster Cluster to add to the graph.
+--
+-- @return |true| if the cluster was added successfully, |false| otherwise.
+--
+function Graph:addCluster(cluster)
+ if not self:findClusterByName(cluster.name) then
+ table.insert(self.clusters, cluster)
+ end
+end
+
+
+
+
+
+--- Returns a string representation of this graph including all nodes and edges.
+--
+-- @ignore This should not appear in the documentation.
+--
+-- @return Graph as string.
+--
+function Graph:__tostring()
+ local tmp = Graph.__tostring
+ Graph.__tostring = nil
+ local result = "Graph<" .. tostring(self) .. ">(("
+ Graph.__tostring = tmp
+
+ local first = true
+ for _,node in ipairs(self.nodes) do
+ if first then first = false else result = result .. ", " end
+ result = result .. tostring(node)
+ end
+ result = result .. "), ("
+ first = true
+ for _,edge in ipairs(self.edges) do
+ if first then first = false else result = result .. ", " end
+ result = result .. tostring(edge)
+ end
+
+ return result .. "))"
+end
+
+
+
+-- Done
+
+return Graph
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/deprecated/Iterators.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/deprecated/Iterators.lua
new file mode 100644
index 0000000000..903291a892
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/deprecated/Iterators.lua
@@ -0,0 +1,91 @@
+-- Copyright 2011 by Jannis Pohlmann
+-- 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$
+
+
+
+--- The Iterators class is a singleton object.
+--
+-- It provides advanced iterators.
+
+local Iterators = {}
+
+-- Namespace
+
+local lib = require("pgf.gd.lib")
+
+
+
+--- Iterator for traversing a \meta{dag} using a topological sorting.
+--
+-- A topological sorting of a directed graph is a linear ordering of its
+-- nodes such that, for every edge |(u,v)|, |u| comes before |v|.
+--
+-- Important note: if performed on a graph with at least one cycle a
+-- topological sorting is impossible. Thus, the nodes returned from the
+-- iterator are not guaranteed to satisfy the ``|u| comes before |v|''
+-- criterion. The iterator may even terminate early or loop forever.
+--
+-- @param graph A directed acyclic graph.
+--
+-- @return An iterator for traversing \meta{graph} in a topological order.
+--
+function Iterators.topologicallySorted(dag)
+ -- track visited edges
+ local deleted_edges = {}
+
+ -- collect all sources (nodes with no incoming edges) of the dag
+ local sources = lib.imap(dag.nodes, function (node) if node:getInDegree() == 0 then return node end end)
+
+ -- return the iterator function
+ return function ()
+ while #sources > 0 do
+ -- fetch the next sink from the queue
+ local source = table.remove(sources, 1)
+
+ -- get its outgoing edges
+ local out_edges = source:getOutgoingEdges()
+
+ -- iterate over all outgoing edges we haven't visited yet
+ for _,edge in ipairs(out_edges) do
+ if not deleted_edges[edge] then
+ -- mark the edge as visited
+ deleted_edges[edge] = true
+
+ -- get the node at the other end of the edge
+ local neighbour = edge:getNeighbour(source)
+
+ -- get a list of all incoming edges of the neighbor that have
+ -- not been visited yet
+ local in_edges = lib.imap(neighbour:getIncomingEdges(),
+ function (edge) if not deleted_edges[edge] then return edge end end)
+
+ -- if there are no such edges then we have a new source
+ if #in_edges == 0 then
+ sources[#sources+1] = neighbour
+ end
+ end
+ end
+
+ -- return the current source
+ return source
+ end
+
+ -- the iterator terminates if there are no sources left
+ return nil
+ end
+end
+
+
+
+-- Done
+
+return Iterators
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/deprecated/Node.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/deprecated/Node.lua
new file mode 100644
index 0000000000..379a65fafd
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/deprecated/Node.lua
@@ -0,0 +1,280 @@
+-- Copyright 2010 by Renée Ahrens, Olof Frahm, Jens Kluttig, Matthias Schulz, Stephan Schuster
+-- Copyright 2011 by Jannis Pohlmann
+-- 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$
+
+
+
+--- The Node class
+--
+--
+
+local Node = {}
+Node.__index = Node
+
+
+-- Namespace
+
+local lib = require "pgf.gd.lib"
+
+
+-- Imports
+
+local Vector = require "pgf.gd.deprecated.Vector"
+
+
+
+--- Creates a new node.
+--
+-- @param values Values to override default node settings.
+-- The following parameters can be set:\par
+-- |name|: The name of the node. It is obligatory to define this.\par
+-- |tex|: Information about the corresponding \TeX\ node.\par
+-- |edges|: Edges adjacent to the node.\par
+-- |pos|: Initial position of the node.\par
+-- |options|: A table of node options passed over from \tikzname.
+--
+-- @return A newly allocated node.
+--
+function Node.new(values)
+ local new = {
+ class = Node,
+ name = nil,
+ tex = {
+ -- texNode = nil,
+ -- maxX = nil,
+ -- minX = nil,
+ -- maxY = nil,
+ -- minY = nil
+ },
+ edges = {},
+ -- pos = nil,
+ options = {},
+ -- growth_direction = nil,
+ -- index = nil,
+ -- event_index = nil,
+ kind = "node",
+ }
+ setmetatable(new, Node)
+ if values then
+ for k,v in pairs(values) do
+ new [k] = v
+ end
+ end
+ if not new.pos then
+ new.pos = Vector.new(2)
+ end
+ return new
+end
+
+
+
+--- Sets the node option \meta{name} to \meta{value}.
+--
+-- @param name Name of the node option to be changed.
+-- @param value New value for the node option \meta{name}.
+--
+function Node:setOption(name, value)
+ self.options[name] = value
+end
+
+
+
+--- Returns the value of the node option \meta{name}.
+--
+-- @param name Name of the node option.
+-- @param graph If this optional argument is given,
+-- in case the option is not set as a node parameter,
+-- we try to look it up as a graph parameter.
+--
+-- @return The value of the node option \meta{name} or |nil|.
+--
+function Node:getOption(name, graph)
+ return lib.lookup_option(name, self, graph)
+end
+
+
+
+--- Computes the width of the node.
+--
+-- @return Width of the node.
+--
+function Node:getTexWidth()
+ return math.abs(self.tex.maxX - self.tex.minX)
+end
+
+
+
+--- Computes the height of the node.
+--
+-- @return Height of the node.
+--
+function Node:getTexHeight()
+ return math.abs(self.tex.maxY - self.tex.minY)
+end
+
+
+
+--- Adds new edge to the node.
+--
+-- @param edge The edge to be added.
+--
+function Node:addEdge(edge)
+ table.insert(self.edges, edge)
+end
+
+
+
+--- Removes an edge from the node.
+--
+-- @param edge The edge to remove.
+--
+function Node:removeEdge(edge)
+ self.edges = lib.imap (self.edges, function(other) if other ~= edge then return other end end)
+end
+
+
+
+--- Counts the adjacent edges of the node.
+--
+-- @return The number of adjacent edges of the node.
+--
+function Node:getDegree()
+ return #self.edges
+end
+
+
+
+--- Returns all edges of the node.
+--
+-- Instead of calling |node:getEdges()| the edges can alternatively be
+-- accessed directly with |node.edges|.
+--
+-- @return All edges of the node.
+--
+function Node:getEdges()
+ return self.edges
+end
+
+
+
+--- Returns the incoming edges of the node. Undefined result for hyperedges.
+--
+-- @param ignore_reversed Optional parameter to consider reversed edges not
+-- reversed for this method call. Defaults to |false|.
+--
+-- @return Incoming edges of the node. This includes undirected edges
+-- and directed edges pointing to the node.
+--
+function Node:getIncomingEdges(ignore_reversed)
+ return lib.imap(self.edges,
+ function (edge)
+ if edge:isHead(self, ignore_reversed) then return edge end
+ end)
+end
+
+
+
+--- Returns the outgoing edges of the node. Undefined result for hyperedges.
+--
+-- @param ignore_reversed Optional parameter to consider reversed edges not
+-- reversed for this method call. Defaults to |false|.
+--
+-- @return Outgoing edges of the node. This includes undirected edges
+-- and directed edges leaving the node.
+--
+function Node:getOutgoingEdges(ignore_reversed)
+ return lib.imap(self.edges,
+ function (edge)
+ if edge:isTail(self, ignore_reversed) then return edge end
+ end)
+end
+
+
+
+--- Returns the number of incoming edges of the node.
+--
+-- @see Node:getIncomingEdges(reversed)
+--
+-- @param ignore_reversed Optional parameter to consider reversed edges not
+-- reversed for this method call. Defaults to |false|.
+--
+-- @return The number of incoming edges of the node.
+--
+function Node:getInDegree(ignore_reversed)
+ return #self:getIncomingEdges(ignore_reversed)
+end
+
+
+
+--- Returns the number of edges starting at the node.
+--
+-- @see Node:getOutgoingEdges()
+--
+-- @param ignore_reversed Optional parameter to consider reversed edges not
+-- reversed for this method call. Defaults to |false|.
+--
+-- @return The number of outgoing edges of the node.
+--
+function Node:getOutDegree(ignore_reversed)
+ return #self:getOutgoingEdges(ignore_reversed)
+end
+
+
+
+--- Creates a shallow copy of the node.
+--
+-- Most notably, the edges adjacent are not preserved in the copy.
+--
+-- @return Copy of the node.
+--
+function Node:copy()
+ local result = lib.copy(self, Node.new())
+ result.edges = {}
+ return result
+end
+
+
+
+--- Compares two nodes by their name.
+--
+-- @ignore This should not appear in the documentation.
+--
+-- @param other Another node to compare with.
+--
+-- @return |true| if both nodes have the same name. |false| otherwise.
+--
+function Node:__eq(object)
+ return self.name == object.name
+end
+
+
+
+--- Returns a formated string representation of the node.
+--
+-- @ignore This should not appear in the documentation.
+--
+-- @return String representation of the node.
+--
+function Node:__tostring()
+ local tmp = Node.__tostring
+ Node.__tostring = nil
+ local result = "Node<" .. tostring(self) .. ">(" .. self.name .. ")"
+ Node.__tostring = tmp
+ return result
+end
+
+
+
+
+-- Done
+
+return Node
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/deprecated/Vector.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/deprecated/Vector.lua
new file mode 100644
index 0000000000..8af7378154
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/deprecated/Vector.lua
@@ -0,0 +1,256 @@
+-- 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$
+
+
+--- Vector class
+--
+-- This class augments a normal array so that:
+--
+-- 1) Several functions like "plus" or "normalize" become available.
+-- 2) You can access the ".x" and ".y" fields to get the fields [1] and [2].
+
+local Vector = {}
+
+
+-- Namespace:
+local lib = require "pgf.gd.lib"
+lib.Vector = Vector
+
+
+-- Class setup
+Vector.__index =
+ function (t, k)
+ if k == "x" then
+ return rawget(t,1)
+ elseif k == "y" then
+ return rawget(t,2)
+ else
+ return rawget(Vector,k)
+ end
+ end
+Vector.__newindex =
+ function (t, k, v)
+ if k == "x" then
+ rawset(t,1,v)
+ elseif k == "y" then
+ rawset(t,2,v)
+ else
+ rawset(t,k,v)
+ end
+ end
+
+
+
+--- Creates a new vector with \meta{n} values using an optional \meta{fill\_function}.
+--
+-- @param n The number of elements of the vector. (If omitted, then 2.)
+-- @param fill_function Optional function that takes a number between 1 and \meta{n}
+-- and is expected to return a value for the corresponding element
+-- of the vector. If omitted, all elements of the vector will
+-- be initialized with 0.
+--
+-- @return A newly-allocated vector with \meta{n} elements.
+--
+function Vector.new(n, fill_function)
+ -- create vector
+ local vector = { }
+ setmetatable(vector, Vector)
+
+ local n = n or 2
+
+ if type(n) == 'table' then
+ for k,v in pairs(n) do
+ vector[k] = v
+ end
+ else
+ -- fill vector elements with values
+ if not fill_function then
+ for i = 1,n do
+ rawset(vector,i,0)
+ end
+ else
+ for i = 1,n do
+ rawset(vector,i,fill_function(i))
+ end
+ end
+ end
+
+ return vector
+end
+
+
+
+--- Creates a copy of the vector that holds the same elements as the original.
+--
+-- @return A newly-allocated copy of the vector holding exactly the same elements.
+--
+function Vector:copy()
+ return Vector.new(#self, function (n) return self[n] end)
+end
+
+
+
+--- Performs a vector addition and returns the result in a new vector.
+--
+-- @param other The vector to add.
+--
+-- @return A new vector with the result of the addition.
+--
+function Vector:plus(other)
+ assert(#self == #other)
+
+ return Vector.new(#self, function (n) return self[n] + other[n] end)
+end
+
+
+
+--- Subtracts two vectors and returns the result in a new vector.
+--
+-- @param other Vector to subtract.
+--
+-- @return A new vector with the result of the subtraction.
+--
+function Vector:minus(other)
+ assert(#self == #other)
+
+ return Vector.new(#self, function (n) return self[n] - other[n] end)
+end
+
+
+
+--- Divides a vector by a scalar value and returns the result in a new vector.
+--
+-- @param scalar Scalar value to divide the vector by.
+--
+-- @return A new vector with the result of the division.
+--
+function Vector:dividedByScalar(scalar)
+ return Vector.new(#self, function (n) return self[n] / scalar end)
+end
+
+
+
+--- Multiplies a vector by a scalar value and returns the result in a new vector.
+--
+-- @param scalar Scalar value to multiply the vector with.
+--
+-- @return A new vector with the result of the multiplication.
+--
+function Vector:timesScalar(scalar)
+ return Vector.new(#self, function (n) return self[n] * scalar end)
+end
+
+
+
+--- Performs the dot product of two vectors and returns the result in a new vector.
+--
+-- @param other Vector to perform the dot product with.
+--
+-- @return A new vector with the result of the dot product.
+--
+function Vector:dotProduct(other)
+ assert(#self == #other)
+
+ local product = 0
+ for n = 1,#self do
+ product = product + self[n] * other[n]
+ end
+ return product
+end
+
+
+
+--- Computes the Euclidean norm of the vector.
+--
+-- @return The Euclidean norm of the vector.
+--
+function Vector:norm()
+ return math.sqrt(self:dotProduct(self))
+end
+
+
+
+--- Normalizes the vector and returns the result in a new vector.
+--
+-- @return Normalized version of the original vector.
+--
+function Vector:normalized()
+ local norm = self:norm()
+ if norm == 0 then
+ return Vector.new(#self)
+ else
+ return self:dividedByScalar(self:norm())
+ end
+end
+
+
+
+--- Updates the values of the vector in-place.
+--
+-- @param update_function A function that is called for each element of the
+-- vector. The elements are replaced by the values
+-- returned from this function.
+--
+function Vector:update(update_function)
+ for i=1,#self do
+ self[i] = update_function(self[i])
+ end
+end
+
+
+
+--- Limits all elements of the vector in-place.
+--
+-- @param limit_function A function that is called for each index/element
+-- pair. It is supposed to return minimum and maximum
+-- values for the element. The element is then clamped
+-- to these values.
+--
+function Vector:limit(limit_function)
+ for i=1,#self do
+ local min, max = limit_function(i, self[i])
+ self[i] = math.max(min, math.min(max, value))
+ end
+end
+
+
+--- Tests whether all elements of two vectors are the same
+--
+-- @param other The other vector
+--
+-- @return true or false
+--
+function Vector:equals(other)
+ if #self ~= #other then
+ return false
+ end
+
+ for n = 1, #self do
+ if self[n] ~= other[n] then
+ return false
+ end
+ end
+
+ return true
+end
+
+
+function Vector:__tostring()
+ return '(' .. table.concat(self, ', ') .. ')'
+end
+
+
+
+
+
+-- Done
+
+return Vector
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc.lua
new file mode 100644
index 0000000000..78b68bbcb1
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc.lua
@@ -0,0 +1,124 @@
+-- Copyright 2013 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$
+
+
+---
+-- The table doc is used for documentation purposes. It is used to
+-- provide lazy documentation for keys, that is, to install
+-- documentation for keys only when this information is requested and
+-- when the documentation is kept in a separate file.
+--
+-- Using the doc facility is easy:
+-- %
+-- \begin{enumerate}
+-- \item In the |declare| statement of the key, you do not provide
+-- fields like |documentation| or |summary|. Rather, you provide the
+-- field |documentation_in|, which gets the name of a Lua file the
+-- will be read whenever one of the fields |documentation|, |summary|,
+-- or |examples| is requested for the key.
+-- \item When the key is requested, |require| will be applied to the
+-- filename given in the |documentation_in| field.
+-- \item In this file, you start with the following code:
+-- %
+--\begin{codeexample}[code only]
+--local doc = require 'pgf.gd.doc'
+--local key = doc.key
+--local documentation = doc.documentation
+--local summary = doc.summary
+--local example = doc.example
+--\end{codeexample}
+-- %
+-- This will setup nice shortcuts for the commands you are going to
+-- use in your file.
+-- \item Next, for each to-be-lazily-documented key, add a block to
+-- the file like the following:
+-- %
+--\begin{codeexample}[code only]
+-- ---
+-- key "my radius"
+-- summary "This key specifies a radius."
+-- documentation
+-- [[
+-- This key is used, whenever...
+-- ]]
+-- example "\tikz \graph [foo layout, my radius=5] { a--b };"
+-- example "\tikz \graph [foo layout, my radius=3] { c--d };"
+--\end{codeexample}
+--
+-- Note that |[[| and |]]| are used in Lua for raw multi-line strings.
+--
+-- The effect of the above code will be that for the key |my radius|
+-- the different field like |summary| or |documentation| get
+-- updated. The |key| function simple ``selects'' a key and subsequent
+-- commands like |summary| will update this key until a different key
+-- is selected through another use of |key|.
+-- \end{enumerate}
+
+local doc = {}
+
+local current_key
+
+
+-- Namespace
+require "pgf.gd".doc = doc
+
+
+-- Imports
+local keys = require "pgf.gd.interface.InterfaceCore".keys
+
+---
+-- Selects the key which will be subsequently updated by the other
+-- functions of this class.
+--
+-- @param key A key.
+
+function doc.key (key)
+ current_key = assert(keys[key], "trying to document not-yet-declared key")
+end
+
+
+---
+-- Updates (replaces) the summary field of the last key selected
+-- through the |key| command.
+--
+-- @param string A (new) summary string.
+
+function doc.summary (string)
+ current_key.summary = string
+end
+
+
+---
+-- Updates (replaces) the documentation field of the last key selected
+-- through the |key| command.
+--
+-- @param string A (new) documentation string. Typically, the |[[|
+-- syntax will be used to specify this string.
+
+function doc.documentation (string)
+ current_key.documentation = string
+end
+
+
+---
+-- Adds an example to the |examples| field of the last key selected
+-- through the |key| command.
+--
+-- @param string An additional example string.
+
+function doc.example (string)
+ local examples = rawget(current_key, "examples") or {}
+ examples[#examples + 1] = string
+ current_key.examples = examples
+end
+
+
+return doc
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/energybased.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/energybased.lua
new file mode 100644
index 0000000000..552dd00dc8
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/energybased.lua
@@ -0,0 +1,19 @@
+-- Copyright 2013 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$
+
+
+
+---
+-- @section subsection {Force-Based Algorithms}
+--
+--
+
+local _ \ No newline at end of file
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/energybased/FMMMLayout.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/energybased/FMMMLayout.lua
new file mode 100644
index 0000000000..fcd5b26fb0
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/energybased/FMMMLayout.lua
@@ -0,0 +1,129 @@
+-- Copyright 2013 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$
+
+
+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 "FMMMLayout"
+summary "The fast multipole multilevel layout algorithm."
+
+documentation
+[[
+|FMMMLayout| implements a force-directed graph drawing
+method suited also for very large graphs. It is based on a
+combination of an efficient multilevel scheme and a strategy for
+approximating the repulsive forces in the system by rapidly
+evaluating potential fields.
+
+The implementation is based on the following publication:
+%
+\begin{itemize}
+ \item Stefan Hachul, Michael J\"unger: Drawing Large Graphs with
+ a Potential-Field-Based Multilevel Algorithm. \emph{12th
+ International Symposium on Graph Drawing 1998 (GD '04)},
+ New York, LNCS 3383, pp. 285--295, 2004.
+\end{itemize}
+]]
+
+example
+[[
+\tikz \graph [FMMMLayout] { a -- {b,c,d} -- e -- a };
+]]
+
+
+example
+[[
+\tikz [nodes={text height=.7em, text depth=.2em,
+ draw=black!20, thick, fill=white, font=\footnotesize},
+ >={Stealth[round,sep]}, rounded corners, semithick]
+ \graph [FMMMLayout, node sep=1mm, variation=2] {
+ "5th Edition" -> { "6th Edition", "PWB 1.0" };
+ "6th Edition" -> { "LSX", "1 BSD", "Mini Unix", "Wollongong", "Interdata" };
+ "Interdata" ->[orient=down] "Unix/TS 3.0",
+ "Interdata" -> { "PWB 2.0", "7th Edition" };
+ "7th Edition" -> { "8th Edition", "32V", "V7M", "Ultrix-11", "Xenix", "UniPlus+" };
+ "V7M" -> "Ultrix-11";
+ "8th Edition" -> "9th Edition";
+ "1 BSD" -> "2 BSD" -> "2.8 BSD" -> { "Ultrix-11", "2.9 BSD" };
+ "32V" -> "3 BSD" -> "4 BSD" -> "4.1 BSD" -> { "4.2 BSD", "2.8 BSD", "8th Edition" };
+ "4.2 BSD" -> { "4.3 BSD", "Ultrix-32" };
+ "PWB 1.0" -> { "PWB 1.2" -> "PWB 2.0", "USG 1.0" -> { "CB Unix 1", "USG 2.0" }};
+ "CB Unix 1" -> "CB Unix 2" -> "CB Unix 3" -> { "Unix/TS++", "PDP-11 Sys V" };
+ { "USG 2.0" -> "USG 3.0", "PWB 2.0", "Unix/TS 1.0" } -> "Unix/TS 3.0";
+ { "Unix/TS++", "CB Unix 3", "Unix/TS 3.0" } ->
+ "TS 4.0" -> "System V.0" -> "System V.2" -> "System V.3";
+ };
+]]
+--------------------------------------------------------------------------------
+
+
+
+--------------------------------------------------------------------------------
+key "FMMMLayout.randSeed"
+summary "Sets the random seed for the |FMMMLayout|."
+documentation
+[[
+By changing this number, you can vary the appearance of the generated
+graph drawing. This key is an alias for |random seed|, which in turn
+can be set by using the |variation| key.
+]]
+
+example
+[[
+\tikz \graph [FMMMLayout, variation=1] { a -- {b,c,d} -- e -- a };
+]]
+example
+[[
+\tikz \graph [FMMMLayout, variation=2] { a -- {b,c,d} -- e -- a };
+]]
+--------------------------------------------------------------------------------
+
+
+
+--------------------------------------------------------------------------------
+key "FMMMLayout.unitEdgeLength"
+summary "The ``ideal'' padding between two nodes."
+
+documentation
+[[
+The algorithm will try to make the padding between any two vertices
+this distance. Naturally, this is not always possible, so, normally,
+distance will actually be different. This key is an alias for the more
+natural |node sep|.
+]]
+
+example
+[[
+\tikz {
+ \graph [FMMMLayout, node sep=1cm] { subgraph C_n[n=6]; };
+
+ \draw [red, ultra thick, |-|] (1.south) -- ++(down:1cm);
+}
+]]
+example
+[[
+\tikz {
+ \graph [FMMMLayout, node sep=5mm] { subgraph C_n[n=6]; };
+
+ \draw [red, ultra thick, |-|] (1.south) -- ++(down:5mm);
+}
+]]
+--------------------------------------------------------------------------------
+
+
+-- Local Variables:
+-- mode:latex
+-- End:
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/energybased/FastMultipoleEmbedder.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/energybased/FastMultipoleEmbedder.lua
new file mode 100644
index 0000000000..453078b130
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/energybased/FastMultipoleEmbedder.lua
@@ -0,0 +1,51 @@
+-- Copyright 2013 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$
+
+
+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 "FastMultipoleEmbedder"
+summary "Implementation of a fast multipole embedder by Martin Gronemann."
+--------------------------------------------------------------------------------
+
+
+--------------------------------------------------------------------------------
+key "FastMultipoleEmbedder.numIterations"
+summary "sets the maximum number of iterations"
+--------------------------------------------------------------------------------
+
+
+--------------------------------------------------------------------------------
+key "FastMultipoleEmbedder.multipolePrec"
+summary "sets the number of coefficients for the expansions. default = 4"
+--------------------------------------------------------------------------------
+
+
+--------------------------------------------------------------------------------
+key "FastMultipoleEmbedder.defaultEdgeLength"
+summary ""
+--------------------------------------------------------------------------------
+
+
+--------------------------------------------------------------------------------
+key "FastMultipoleEmbedder.defaultNodeSize"
+summary ""
+--------------------------------------------------------------------------------
+
+
+-- Local Variables:
+-- mode:latex
+-- End:
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/energybased/GEMLayout.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/energybased/GEMLayout.lua
new file mode 100644
index 0000000000..d0b94c2bb7
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/energybased/GEMLayout.lua
@@ -0,0 +1,103 @@
+-- Copyright 2013 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$
+
+
+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 "GEMLayout"
+summary "The energy-based GEM layout algorithm."
+
+documentation [[
+ The implementation used in |GEMLayout| is based on the following publication:
+ %
+ \begin{itemize}
+ \item Arne Frick, Andreas Ludwig, Heiko Mehldau: \emph{A Fast Adaptive Layout
+ Algorithm for Undirected Graphs.} Proc. Graph Drawing 1994,
+ LNCS 894, pp. 388-403, 1995.
+ \end{itemize}
+]]
+--------------------------------------------------------------------------------
+
+
+--------------------------------------------------------------------------------
+key "GEMLayout.numberOfRounds"
+summary "Sets the maximal number of round per node."
+--------------------------------------------------------------------------------
+
+
+--------------------------------------------------------------------------------
+key "GEMLayout.minimalTemperature"
+summary "Sets the minimal temperature."
+--------------------------------------------------------------------------------
+
+
+--------------------------------------------------------------------------------
+key "GEMLayout.initialTemperature"
+summary "Sets the initial temperature; must be $\\ge$ |minimalTemperature|."
+--------------------------------------------------------------------------------
+
+
+--------------------------------------------------------------------------------
+key "GEMLayout.gravitationalConstant"
+summary "Sets the gravitational constant; must be $\\ge 0$."
+--------------------------------------------------------------------------------
+
+
+--------------------------------------------------------------------------------
+key "GEMLayout.desiredLength"
+summary "Sets the desired edge length; must be $\\ge 0$."
+--------------------------------------------------------------------------------
+
+
+--------------------------------------------------------------------------------
+key "GEMLayout.maximalDisturbance"
+summary "Sets the maximal disturbance; must be $\\ge 0$."
+--------------------------------------------------------------------------------
+
+
+--------------------------------------------------------------------------------
+key "GEMLayout.rotationAngle"
+summary "Sets the opening angle for rotations ($0 \\le x \\le \\pi / 2$)."
+--------------------------------------------------------------------------------
+
+
+--------------------------------------------------------------------------------
+key "GEMLayout.oscillationAngle"
+summary "Sets the opening angle for oscillations ($0 \\le x \\le \\pi / 2$)."
+--------------------------------------------------------------------------------
+
+
+--------------------------------------------------------------------------------
+key "GEMLayout.rotationSensitivity"
+summary "Sets the rotation sensitivity ($0 \\le x \\le 1$)."
+--------------------------------------------------------------------------------
+
+
+--------------------------------------------------------------------------------
+key "GEMLayout.oscillationSensitivity"
+summary "Sets the oscillation sensitivity ($0 \\le x \\le 1$)."
+--------------------------------------------------------------------------------
+
+
+--------------------------------------------------------------------------------
+key "GEMLayout.attractionFormula"
+summary "sets the formula for attraction (1 = Fruchterman / Reingold, 2 = GEM)."
+--------------------------------------------------------------------------------
+
+
+-- Local Variables:
+-- mode:latex
+-- End:
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/energybased/MultilevelLayout.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/energybased/MultilevelLayout.lua
new file mode 100644
index 0000000000..6210c93941
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/energybased/MultilevelLayout.lua
@@ -0,0 +1,33 @@
+-- Copyright 2013 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$
+
+
+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 "MultilevelLayout"
+summary "A wrapper for the multilevel layout computation using the modular multi-level mixer."
+
+example [[
+\tikz \graph [MultilevelLayout] {
+ a -- b -- c -- a -- d -- e -- f -- a
+};
+]]
+--------------------------------------------------------------------------------
+
+
+-- Local Variables:
+-- mode:latex
+-- End:
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/energybased/SpringEmbedderFR.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/energybased/SpringEmbedderFR.lua
new file mode 100644
index 0000000000..ae3cf9c8ef
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/energybased/SpringEmbedderFR.lua
@@ -0,0 +1,53 @@
+-- Copyright 2013 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$
+
+
+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 "SpringEmbedderFR"
+summary "The spring-embedder layout algorithm by Fruchterman and Reingold."
+
+documentation [[
+ The implementation used in SpringEmbedderFR is based on the following
+ publication:
+
+ Thomas M. J. Fruchterman, Edward M. Reingold: \emph{Graph Drawing by Force-directed
+ Placement}. Software - Practice and Experience 21(11), pp. 1129--1164, 1991.
+]]
+--------------------------------------------------------------------------------
+
+
+--------------------------------------------------------------------------------
+key "SpringEmbedderFR.iterations"
+summary "Sets the number of iterations."
+--------------------------------------------------------------------------------
+
+
+--------------------------------------------------------------------------------
+key "SpringEmbedderFR.noise"
+summary "Sets the parameter noise."
+--------------------------------------------------------------------------------
+
+
+--------------------------------------------------------------------------------
+key "SpringEmbedderFR.scaleFunctionFactor"
+summary "Sets the scale function factor."
+--------------------------------------------------------------------------------
+
+
+-- Local Variables:
+-- mode:latex
+-- End:
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/energybased/SpringEmbedderFRExact.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/energybased/SpringEmbedderFRExact.lua
new file mode 100644
index 0000000000..6286e9f395
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/energybased/SpringEmbedderFRExact.lua
@@ -0,0 +1,57 @@
+-- Copyright 2013 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$
+
+
+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 "SpringEmbedderFRExact"
+summary "Declaration of Spring-Embedder (Fruchterman,Reingold) algorithm with exact force computations."
+--------------------------------------------------------------------------------
+
+
+--------------------------------------------------------------------------------
+key "SpringEmbedderFRExact.iterations"
+summary "Sets the number of iterations."
+--------------------------------------------------------------------------------
+
+
+--------------------------------------------------------------------------------
+key "SpringEmbedderFRExact.noise"
+summary "Sets the parameter noise."
+--------------------------------------------------------------------------------
+
+
+--------------------------------------------------------------------------------
+key "SpringEmbedderFRExact.coolingFunction"
+summary "Sets the parameter coolingFunction to |factor| or to |logarithmic|."
+--------------------------------------------------------------------------------
+
+
+--------------------------------------------------------------------------------
+key "SpringEmbedderFRExact.idealEdgeLength"
+summary "Sets the ideal edge length to a length."
+--------------------------------------------------------------------------------
+
+
+--------------------------------------------------------------------------------
+key "SpringEmbedderFRExact.convTolerance"
+summary ""
+--------------------------------------------------------------------------------
+
+
+-- Local Variables:
+-- mode:latex
+-- End:
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/energybased/SpringEmbedderKK.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/energybased/SpringEmbedderKK.lua
new file mode 100644
index 0000000000..a4fd46aa77
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/energybased/SpringEmbedderKK.lua
@@ -0,0 +1,62 @@
+-- Copyright 2013 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$
+
+
+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 "SpringEmbedderKK"
+summary "The spring embedder of Kamada and Kawai"
+
+documentation [[
+ The implementation used in |SpringEmbedderKK| is based on
+ the following publication:
+
+ Tomihisa Kamada, Satoru Kawai: \emph{An Algorithm for Drawing
+ General Undirected Graphs.} Information Processing Letters 31, pp. 7--15, 1989.
+
+ There are some parameters that can be tuned to optimize the
+ algorithm's behavior regarding runtime and layout quality.
+ First of all note that the algorithm uses all pairs shortest path
+ to compute the graph theoretic distance. This can be done either
+ with BFS (ignoring node sizes) in quadratic time or by using
+ e.g. Floyd's algorithm in cubic time with given edge lengths
+ that may reflect the node sizes. Also |m_computeMaxIt| decides
+ if the computation is stopped after a fixed maximum number of
+ iterations. The desirable edge length can either be set or computed
+ from the graph and the given layout.
+]]
+--------------------------------------------------------------------------------
+
+
+--------------------------------------------------------------------------------
+key "SpringEmbedderKK.stopTolerance"
+summary "Sets the value for the stop tolerance."
+documentation [[
+ Below this value, the system is regarded stable (balanced) and the
+ optimization stopped.
+]]
+--------------------------------------------------------------------------------
+
+
+--------------------------------------------------------------------------------
+key "SpringEmbedderKK.desLength"
+summary "Sets desirable edge length directly"
+--------------------------------------------------------------------------------
+
+
+-- Local Variables:
+-- mode:latex
+-- End:
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/energybased/multilevelmixer/BarycenterPlacer.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/energybased/multilevelmixer/BarycenterPlacer.lua
new file mode 100644
index 0000000000..e658a896bd
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/energybased/multilevelmixer/BarycenterPlacer.lua
@@ -0,0 +1,33 @@
+-- Copyright 2013 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$
+
+
+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 "BarycenterPlacer"
+summary "?"
+--------------------------------------------------------------------------------
+
+
+--------------------------------------------------------------------------------
+key "BarycenterPlacer.weightedPositionPriority"
+summary ""
+--------------------------------------------------------------------------------
+
+
+-- Local Variables:
+-- mode:latex
+-- End:
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/energybased/multilevelmixer/CirclePlacer.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/energybased/multilevelmixer/CirclePlacer.lua
new file mode 100644
index 0000000000..e6f06add57
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/energybased/multilevelmixer/CirclePlacer.lua
@@ -0,0 +1,45 @@
+-- Copyright 2013 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$
+
+
+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 "CirclePlacer"
+summary "?"
+--------------------------------------------------------------------------------
+
+
+--------------------------------------------------------------------------------
+key "CirclePlacer.circleSize"
+summary ""
+--------------------------------------------------------------------------------
+
+
+--------------------------------------------------------------------------------
+key "CirclePlacer.radiusFixed"
+summary ""
+--------------------------------------------------------------------------------
+
+
+--------------------------------------------------------------------------------
+key "CirclePlacer.nodeSelection"
+summary ""
+--------------------------------------------------------------------------------
+
+
+-- Local Variables:
+-- mode:latex
+-- End:
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/energybased/multilevelmixer/EdgeCoverMerger.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/energybased/multilevelmixer/EdgeCoverMerger.lua
new file mode 100644
index 0000000000..e8723272ee
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/energybased/multilevelmixer/EdgeCoverMerger.lua
@@ -0,0 +1,33 @@
+-- Copyright 2013 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$
+
+
+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 "EdgeCoverMerger"
+summary "?"
+--------------------------------------------------------------------------------
+
+
+--------------------------------------------------------------------------------
+key "EdgeCoverMerger.factor"
+summary ""
+--------------------------------------------------------------------------------
+
+
+-- Local Variables:
+-- mode:latex
+-- End:
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/energybased/multilevelmixer/IndependentSetMerger.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/energybased/multilevelmixer/IndependentSetMerger.lua
new file mode 100644
index 0000000000..81e2f37db0
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/energybased/multilevelmixer/IndependentSetMerger.lua
@@ -0,0 +1,33 @@
+-- Copyright 2013 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$
+
+
+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 "IndependentSetMerger"
+summary "?"
+--------------------------------------------------------------------------------
+
+
+--------------------------------------------------------------------------------
+key "IndependentSetMerger.searchDepthBase"
+summary ""
+--------------------------------------------------------------------------------
+
+
+-- Local Variables:
+-- mode:latex
+-- End:
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/energybased/multilevelmixer/LocalBiconnectedMerger.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/energybased/multilevelmixer/LocalBiconnectedMerger.lua
new file mode 100644
index 0000000000..7fbca25e81
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/energybased/multilevelmixer/LocalBiconnectedMerger.lua
@@ -0,0 +1,33 @@
+-- Copyright 2013 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$
+
+
+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 "LocalBiconnectedMerger"
+summary "?"
+--------------------------------------------------------------------------------
+
+
+--------------------------------------------------------------------------------
+key "LocalBiconnectedMerger.factor"
+summary ""
+--------------------------------------------------------------------------------
+
+
+-- Local Variables:
+-- mode:latex
+-- End:
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/energybased/multilevelmixer/MatchingMerger.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/energybased/multilevelmixer/MatchingMerger.lua
new file mode 100644
index 0000000000..e966b49056
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/energybased/multilevelmixer/MatchingMerger.lua
@@ -0,0 +1,33 @@
+-- Copyright 2013 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$
+
+
+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 "MatchingMerger"
+summary "?"
+--------------------------------------------------------------------------------
+
+
+--------------------------------------------------------------------------------
+key "MatchingMerger.selectByNodeMass"
+summary ""
+--------------------------------------------------------------------------------
+
+
+-- Local Variables:
+-- mode:latex
+-- End:
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/energybased/multilevelmixer/MedianPlacer.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/energybased/multilevelmixer/MedianPlacer.lua
new file mode 100644
index 0000000000..0b9d967db9
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/energybased/multilevelmixer/MedianPlacer.lua
@@ -0,0 +1,27 @@
+-- Copyright 2013 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$
+
+
+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 "MedianPlacer"
+summary "?"
+--------------------------------------------------------------------------------
+
+
+-- Local Variables:
+-- mode:latex
+-- End:
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/energybased/multilevelmixer/RandomMerger.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/energybased/multilevelmixer/RandomMerger.lua
new file mode 100644
index 0000000000..71bdbc7340
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/energybased/multilevelmixer/RandomMerger.lua
@@ -0,0 +1,33 @@
+-- Copyright 2013 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$
+
+
+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 "RandomMerger"
+summary "?"
+--------------------------------------------------------------------------------
+
+
+--------------------------------------------------------------------------------
+key "RandomMerger.factor"
+summary ""
+--------------------------------------------------------------------------------
+
+
+-- Local Variables:
+-- mode:latex
+-- End:
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/energybased/multilevelmixer/RandomPlacer.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/energybased/multilevelmixer/RandomPlacer.lua
new file mode 100644
index 0000000000..c26946d4e1
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/energybased/multilevelmixer/RandomPlacer.lua
@@ -0,0 +1,33 @@
+-- Copyright 2013 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$
+
+
+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 "RandomPlacer"
+summary "?"
+--------------------------------------------------------------------------------
+
+
+--------------------------------------------------------------------------------
+key "RandomPlacer.circleSize"
+summary ""
+--------------------------------------------------------------------------------
+
+
+-- Local Variables:
+-- mode:latex
+-- End:
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/energybased/multilevelmixer/SolarMerger.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/energybased/multilevelmixer/SolarMerger.lua
new file mode 100644
index 0000000000..76b084fa67
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/energybased/multilevelmixer/SolarMerger.lua
@@ -0,0 +1,39 @@
+-- Copyright 2013 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$
+
+
+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 "SolarMerger"
+summary "?"
+--------------------------------------------------------------------------------
+
+
+--------------------------------------------------------------------------------
+key "SolarMerger.simple"
+summary ""
+--------------------------------------------------------------------------------
+
+
+--------------------------------------------------------------------------------
+key "SolarMerger.massAsNodeRadius"
+summary ""
+--------------------------------------------------------------------------------
+
+
+-- Local Variables:
+-- mode:latex
+-- End:
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/energybased/multilevelmixer/SolarPlacer.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/energybased/multilevelmixer/SolarPlacer.lua
new file mode 100644
index 0000000000..530ddbc0d8
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/energybased/multilevelmixer/SolarPlacer.lua
@@ -0,0 +1,27 @@
+-- Copyright 2013 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$
+
+
+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 "SolarPlacer"
+summary "?"
+--------------------------------------------------------------------------------
+
+
+-- Local Variables:
+-- mode:latex
+-- End:
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/energybased/multilevelmixer/ZeroPlacer.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/energybased/multilevelmixer/ZeroPlacer.lua
new file mode 100644
index 0000000000..6f579d0a45
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/energybased/multilevelmixer/ZeroPlacer.lua
@@ -0,0 +1,33 @@
+-- Copyright 2013 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$
+
+
+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 "ZeroPlacer"
+summary "?"
+--------------------------------------------------------------------------------
+
+
+--------------------------------------------------------------------------------
+key "ZeroPlacer.randomRange"
+summary ""
+--------------------------------------------------------------------------------
+
+
+-- Local Variables:
+-- mode:latex
+-- End:
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/layered.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/layered.lua
new file mode 100644
index 0000000000..ffd3ea844d
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/layered.lua
@@ -0,0 +1,19 @@
+-- Copyright 2013 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$
+
+
+
+---
+-- @section subsection {Algorithms for Drawing Layered Graphs}
+--
+--
+
+local _ \ No newline at end of file
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/layered/BarycenterHeuristic.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/layered/BarycenterHeuristic.lua
new file mode 100644
index 0000000000..5e4138eece
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/layered/BarycenterHeuristic.lua
@@ -0,0 +1,23 @@
+-- Copyright 2013 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$
+
+
+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 "BarycenterHeuristic"
+summary "The barycenter heuristic for 2-layer crossing minimization."
+--------------------------------------------------------------------
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/layered/CoffmanGrahamRanking.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/layered/CoffmanGrahamRanking.lua
new file mode 100644
index 0000000000..fd13e9bd4b
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/layered/CoffmanGrahamRanking.lua
@@ -0,0 +1,35 @@
+-- Copyright 2013 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$
+
+
+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 "CoffmanGrahamRanking"
+summary "The ranking algorithm due to Coffman and Graham."
+documentation
+[[
+|CoffmanGrahamRanking| implements a node ranking algorithm based on
+the Coffman--Graham scheduling algorithm, which can be used as first
+phase in |SugiyamaLayout|. The aim of the algorithm is to ensure that
+the height of the ranking (the number of layers) is kept small.
+]]
+--------------------------------------------------------------------
+
+
+--------------------------------------------------------------------
+key "CoffmanGrahamRanking.width"
+summary "A mysterious width parameter..."
+--------------------------------------------------------------------
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/layered/DfsAcyclicSubgraph.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/layered/DfsAcyclicSubgraph.lua
new file mode 100644
index 0000000000..eced60b2ed
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/layered/DfsAcyclicSubgraph.lua
@@ -0,0 +1,27 @@
+-- Copyright 2013 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$
+
+
+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 "DfsAcyclicSubgraph"
+summary "DFS-based algorithm for computing a maximal acyclic subgraph."
+documentation
+[[
+The algorithm simply removes all DFS-backedges and works in linear-time.
+]]
+--------------------------------------------------------------------
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/layered/FastHierarchyLayout.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/layered/FastHierarchyLayout.lua
new file mode 100644
index 0000000000..a7baad7e10
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/layered/FastHierarchyLayout.lua
@@ -0,0 +1,99 @@
+-- Copyright 2013 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$
+
+
+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 "FastHierarchyLayout"
+summary "Coordinate assignment phase for the Sugiyama algorithm by Buchheim et al."
+documentation
+[[
+This class implements a hierarchy layout algorithm, that is, it
+layouts hierarchies with a given order of nodes on each
+layer. It is used as a third phase of the Sugiyama algorithm.
+
+All edges of the layout will have at most two
+bends. Additionally, for each edge having exactly two bends, the
+segment between them is drawn vertically. This applies in
+particular to the long edges arising in the first phase of the
+Sugiyama algorithm.
+
+The implementation is based on:
+%
+\begin{itemize}
+ \item
+ Christoph Buchheim, Michael Jünger, Sebastian Leipert: A Fast
+ Layout Algorithm for k-Level Graphs. \emph{Proc. Graph
+ Drawing 2000}, volume 1984 of LNCS, pages 229--240, 2001.
+\end{itemize}
+]]
+
+example
+[[
+\tikz \graph [SugiyamaLayout, FastHierarchyLayout] {
+ a -- {b,c,d} -- e -- a;
+};
+]]
+--------------------------------------------------------------------
+
+
+
+--------------------------------------------------------------------
+key "FastHierarchyLayout.fixedLayerDistance"
+summary "If true, the distance between neighbored layers is fixed, otherwise variable."
+--------------------------------------------------------------------
+
+
+
+
+--------------------------------------------------------------------
+key "FastHierarchyLayout.layerDistance"
+summary "Separation distance (padding) between two consecutive layers."
+
+documentation
+[[
+Sets the (minimum?) padding between nodes on two consecutive
+layers. It defaults to the sum of the keys
+|level pre sep| and |level post sep|.
+]]
+
+example
+[[
+\tikz \graph [SugiyamaLayout, FastHierarchyLayout,
+ level sep=1cm] {
+ a -- {b,c,d} -- e -- a;
+};
+]]
+--------------------------------------------------------------------
+
+--------------------------------------------------------------------
+key "FastHierarchyLayout.nodeDistance"
+summary "Separation distance (padding) between two consecutive nodes on the same layer."
+
+documentation
+[[
+Sets the (minimum?) padding between sibling nodes. It defaults to the
+sum of the keys |sibling pre sep| and |sibling post sep|.
+]]
+
+example
+[[
+\tikz \graph [SugiyamaLayout, FastHierarchyLayout,
+ sibling sep=5mm] {
+ a -- {b,c,d} -- e -- a;
+};
+]]
+--------------------------------------------------------------------
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/layered/FastSimpleHierarchyLayout.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/layered/FastSimpleHierarchyLayout.lua
new file mode 100644
index 0000000000..4af6daa620
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/layered/FastSimpleHierarchyLayout.lua
@@ -0,0 +1,99 @@
+-- Copyright 2013 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$
+
+
+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 "FastSimpleHierarchyLayout"
+summary "Coordinate assignment phase for the Sugiyama algorithm by Ulrik Brandes and Boris Köpf."
+
+documentation
+[[
+This class implements a hierarchy layout algorithm, that is, it
+layouts hierarchies with a given order of nodes on each
+layer. It is used as a third phase of the Sugiyama algorithm.
+
+The algorithm runs in three phases:
+%
+\begin{enumerate}
+ \item Alignment (4x)
+ \item Horizontal Compactation (4x)
+ \item Balancing
+\end{enumerate}
+%
+The alignment and horizontal compactification phases are calculated
+downward, upward, left-to-right and right-to-left. The four
+resulting layouts are combined in a balancing step.
+
+Warning: The implementation is known to not always produce a
+correct layout. Therefore this Algorithm is for testing purpose
+only.
+
+The implementation is based on:
+%
+\begin{itemize}
+ \item
+ Ulrik Brandes, Boris Köpf: Fast and Simple Horizontal
+ Coordinate Assignment. \emph{LNCS} 2002, Volume 2265/2002,
+ pp. 33--36
+\end{itemize}
+]]
+
+example
+[[
+\tikz \graph [SugiyamaLayout, FastSimpleHierarchyLayout] {
+ a -- {b,c,d} -- e -- a;
+};
+]]
+--------------------------------------------------------------------
+
+
+
+--------------------------------------------------------------------
+key "FastSimpleHierarchyLayout.layerDistance"
+summary "Distance between the centers of nodes of two consecutive layers."
+
+documentation
+[[
+Sets the (minimum?) distance between nodes on two consecutive
+layers. It defaults to the key |level distance|.
+]]
+example
+[[
+\tikz \graph [SugiyamaLayout, FastSimpleHierarchyLayout,
+ level distance=2cm] {
+ a -- {b,c,d} -- e -- a;
+};
+]]
+--------------------------------------------------------------------
+
+--------------------------------------------------------------------
+key "FastSimpleHierarchyLayout.siblingDistance"
+summary "Distance between the centers of nodes of sibling nodes."
+
+documentation
+[[
+Sets the (minimum?) padding between sibling nodes. It defaults to
+|sibling distance|.
+]]
+example
+[[
+\tikz \graph [SugiyamaLayout, FastSimpleHierarchyLayout,
+ sibling distance=5mm] {
+ a -- {b,c,d} -- e -- a;
+};
+]]
+--------------------------------------------------------------------
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/layered/GreedyCycleRemoval.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/layered/GreedyCycleRemoval.lua
new file mode 100644
index 0000000000..d2f1d8eeea
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/layered/GreedyCycleRemoval.lua
@@ -0,0 +1,27 @@
+-- Copyright 2013 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$
+
+
+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 "GreedyCycleRemoval"
+summary "Greedy algorithm for computing a maximal acyclic subgraph."
+documentation
+[[
+ The algorithm applies a greedy heuristic to compute a maximal
+ acyclic subgraph and works in linear-time.
+]]
+--------------------------------------------------------------------
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/layered/GreedyInsertHeuristic.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/layered/GreedyInsertHeuristic.lua
new file mode 100644
index 0000000000..a8cd94ef19
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/layered/GreedyInsertHeuristic.lua
@@ -0,0 +1,23 @@
+-- Copyright 2013 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$
+
+
+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 "GreedyInsertHeuristic"
+summary "The greedy-insert heuristic for 2-layer crossing minimization."
+--------------------------------------------------------------------
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/layered/LongestPathRanking.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/layered/LongestPathRanking.lua
new file mode 100644
index 0000000000..3d98fbe9c6
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/layered/LongestPathRanking.lua
@@ -0,0 +1,57 @@
+-- Copyright 2013 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$
+
+
+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 "LongestPathRanking"
+summary "The longest-path ranking algorithm."
+documentation
+[[
+ |LongestPathRanking| implements the well-known longest-path ranking
+ algorithm, which can be used as first phase in |SugiyamaLayout|. The
+ implementation contains a special optimization for reducing edge
+ lengths, as well as special treatment of mixed-upward graphs (for
+ instance, \textsc{uml} class diagrams).
+]]
+--------------------------------------------------------------------
+
+
+--------------------------------------------------------------------
+key "LongestPathRanking.separateDeg0Layer"
+summary "If set to true, isolated nodes are placed on a separate layer."
+--------------------------------------------------------------------
+
+
+--------------------------------------------------------------------
+key "LongestPathRanking.separateMultiEdges"
+summary "If set to true, multi-edges will span at least two layers."
+--------------------------------------------------------------------
+
+
+
+--------------------------------------------------------------------
+key "LongestPathRanking.optimizeEdgeLength"
+summary
+[[
+ If set to true the ranking algorithm tries to reduce edge
+ length even if this might increase the height of the layout. Choose
+ false, if the longest-path ranking known from the literature should be
+ used.
+]]
+--------------------------------------------------------------------
+
+
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/layered/MedianHeuristic.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/layered/MedianHeuristic.lua
new file mode 100644
index 0000000000..b37480ac78
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/layered/MedianHeuristic.lua
@@ -0,0 +1,24 @@
+-- Copyright 2013 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$
+
+
+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 "MedianHeuristic"
+summary "The median heuristic for 2-layer crossing minimization."
+--------------------------------------------------------------------
+
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/layered/OptimalRanking.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/layered/OptimalRanking.lua
new file mode 100644
index 0000000000..25a8dd5262
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/layered/OptimalRanking.lua
@@ -0,0 +1,35 @@
+-- Copyright 2013 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$
+
+
+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 "OptimalRanking"
+summary "The optimal ranking algorithm."
+documentation
+[[
+ The |OptimalRanking| implements the LP-based algorithm for
+ computing a node ranking with minimal edge lengths, which can
+ be used as first phase in |SugiyamaLayout|.
+]]
+--------------------------------------------------------------------
+
+
+
+--------------------------------------------------------------------
+key "OptimalRanking.separateMultiEdges"
+summary "If set to true, multi-edges will span at least two layers."
+--------------------------------------------------------------------
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/layered/SiftingHeuristic.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/layered/SiftingHeuristic.lua
new file mode 100644
index 0000000000..d0815a5b76
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/layered/SiftingHeuristic.lua
@@ -0,0 +1,34 @@
+-- Copyright 2013 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$
+
+
+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 "SiftingHeuristic"
+summary "The sifting heuristic for 2-layer crossing minimization."
+--------------------------------------------------------------------
+
+
+--------------------------------------------------------------------
+key "SiftingHeuristic.strategy"
+summary "Sets a so-called ``sifting strategy''."
+documentation
+[[
+ The following values are permissible: |left_to_right|, |desc_degree|,
+ and |random|.
+]]
+--------------------------------------------------------------------
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/layered/SplitHeuristic.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/layered/SplitHeuristic.lua
new file mode 100644
index 0000000000..1579c7153d
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/layered/SplitHeuristic.lua
@@ -0,0 +1,24 @@
+-- Copyright 2013 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$
+
+
+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 "SplitHeuristic"
+summary "The split heuristic for 2-layer crossing minimization."
+--------------------------------------------------------------------
+
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/layered/SugiyamaLayout.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/layered/SugiyamaLayout.lua
new file mode 100644
index 0000000000..ec75fa7e6e
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/layered/SugiyamaLayout.lua
@@ -0,0 +1,113 @@
+-- Copyright 2013 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$
+
+
+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 "SugiyamaLayout"
+summary "The OGDF implementation of the Sugiyama algorithm."
+
+documentation [[
+ This layout represents a customizable implementation of Sugiyama's
+ layout algorithm. The implementation used in |SugiyamaLayout| is based
+ on the following publications:
+
+ \begin{itemize}
+ \item Emden R. Gansner, Eleftherios Koutsofios, Stephen
+ C. North, Kiem-Phong Vo: A technique for drawing directed
+ graphs. \emph{IEEE Trans. Software Eng.} 19(3):214--230, 1993.
+ \item Georg Sander: \emph{Layout of compound directed graphs.}
+ Technical Report, Universität des Saarlandes, 1996.
+ \end{itemize}
+]]
+
+example
+[[
+\tikz \graph [SugiyamaLayout] { a -- {b,c,d} -- e -- a };
+]]
+
+example
+[[
+\tikz \graph [SugiyamaLayout, grow=right] {
+ a -- {b,c,d} -- e -- a
+};
+]]
+
+example
+[[
+\tikz [nodes={text height=.7em, text depth=.2em,
+ draw=black!20, thick, fill=white, font=\footnotesize},
+ >={Stealth[round,sep]}, rounded corners, semithick]
+ \graph [SugiyamaLayout, FastSimpleHierarchyLayout, grow=-80,
+ level distance=1.5cm, sibling distance=7mm] {
+ "5th Edition" -> { "6th Edition", "PWB 1.0" };
+ "6th Edition" -> { "LSX", "1 BSD", "Mini Unix", "Wollongong", "Interdata" };
+ "Interdata" -> { "Unix/TS 3.0", "PWB 2.0", "7th Edition" };
+ "7th Edition" -> { "8th Edition", "32V", "V7M", "Ultrix-11", "Xenix", "UniPlus+" };
+ "V7M" -> "Ultrix-11";
+ "8th Edition" -> "9th Edition";
+ "1 BSD" -> "2 BSD" -> "2.8 BSD" -> { "Ultrix-11", "2.9 BSD" };
+ "32V" -> "3 BSD" -> "4 BSD" -> "4.1 BSD" -> { "4.2 BSD", "2.8 BSD", "8th Edition" };
+ "4.2 BSD" -> { "4.3 BSD", "Ultrix-32" };
+ "PWB 1.0" -> { "PWB 1.2" -> "PWB 2.0", "USG 1.0" -> { "CB Unix 1", "USG 2.0" }};
+ "CB Unix 1" -> "CB Unix 2" -> "CB Unix 3" -> { "Unix/TS++", "PDP-11 Sys V" };
+ { "USG 2.0" -> "USG 3.0", "PWB 2.0", "Unix/TS 1.0" } -> "Unix/TS 3.0";
+ { "Unix/TS++", "CB Unix 3", "Unix/TS 3.0" } ->
+ "TS 4.0" -> "System V.0" -> "System V.2" -> "System V.3";
+ };
+]]
+--------------------------------------------------------------------------------
+
+
+
+--------------------------------------------------------------------------------
+key "SugiyamaLayout.runs"
+summary "Determines, how many times the crossing minimization is repeated."
+documentation
+[[
+Each repetition (except for the first) starts with
+randomly permuted nodes on each layer. Deterministic behavior can
+be achieved by setting |SugiyamaLayout.runs| to 1.
+]]
+--------------------------------------------------------------------------------
+
+
+
+--------------------------------------------------------------------------------
+key "SugiyamaLayout.transpose"
+documentation [[
+ Determines whether the transpose step is performed
+ after each 2-layer crossing minimization; this step tries to
+ reduce the number of crossings by switching neighbored nodes on
+ a layer.
+]]
+--------------------------------------------------------------------------------
+
+
+
+--------------------------------------------------------------------------------
+key "SugiyamaLayout.fails"
+documentation [[
+ The number of times that the number of crossings may
+ not decrease after a complete top-down bottom-up traversal,
+ before a run is terminated.
+]]
+--------------------------------------------------------------------------------
+
+
+-- Local Variables:
+-- mode:latex
+-- End:
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/misclayout.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/misclayout.lua
new file mode 100644
index 0000000000..9f5f291461
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/misclayout.lua
@@ -0,0 +1,19 @@
+-- Copyright 2013 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$
+
+
+
+---
+-- @section subsection {Miscellaneous Algorithms for Graph Drawing}
+--
+--
+
+local _ \ No newline at end of file
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/misclayout/BalloonLayout.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/misclayout/BalloonLayout.lua
new file mode 100644
index 0000000000..2a250ecece
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/misclayout/BalloonLayout.lua
@@ -0,0 +1,48 @@
+-- Copyright 2013 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$
+
+
+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 "BalloonLayout"
+summary "A ``balloon layout''."
+
+documentation
+[[
+This algorithm computes a radial (balloon) layout based on a
+spanning tree. The algorithm is partially based on the paper
+\emph{On Balloon Drawings of Rooted Trees} by Lin and Yen and on
+\emph{Interacting with Huge Hierarchies: Beyond Cone Trees} by
+Carriere and Kazman.
+]]
+
+example
+[[
+\tikz \graph [BalloonLayout] { a -- {b,c,d -- {e,f,h,h,i}, j -- k -- {l,m,n}} };
+]]
+--------------------------------------------------------------------------------
+
+
+
+--------------------------------------------------------------------------------
+key "BalloonLayout.evenAngles"
+summary "Subtrees may be assigned even angles or angles depending on their size."
+--------------------------------------------------------------------------------
+
+
+-- Local Variables:
+-- mode:latex
+-- End: \ No newline at end of file
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/misclayout/CircularLayout.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/misclayout/CircularLayout.lua
new file mode 100644
index 0000000000..c5e2cf7a6c
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/misclayout/CircularLayout.lua
@@ -0,0 +1,95 @@
+-- Copyright 2013 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$
+
+
+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 "CircularLayout"
+summary "The circular layout algorithm."
+
+documentation
+[[
+The implementation used in CircularLayout is based on the following publication:
+%
+\begin{itemize}
+ \item Ugur Dogrus\"oz, Brendan Madden, Patrick Madden: Circular
+ Layout in the Graph Layout Toolkit. \emph{Proc. Graph Drawing 1996,}
+ LNCS 1190, pp. 92--100, 1997.
+\end{itemize}
+]]
+
+example
+[[
+\tikz \graph [CircularLayout] {
+ a -- b -- c -- a -- d -- e -- f -- g -- d;
+ b -- {x,y,z};
+};
+]]
+--------------------------------------------------------------------------------
+
+
+
+--------------------------------------------------------------------------------
+key "CircularLayout.minDistCircle"
+summary "The minimal padding between nodes on a circle."
+
+documentation "This is an alias for |part sep|."
+
+example
+[[
+\tikz \graph [CircularLayout, part sep=1cm] {
+ a -- b -- c -- a -- d -- e -- f -- g -- d;
+ b -- {x,y,z};
+};
+]]
+--------------------------------------------------------------------------------
+
+
+--------------------------------------------------------------------------------
+key "CircularLayout.minDistLevel"
+summary "The minimal padding between nodes on different levels."
+
+documentation "This is an alias for |layer sep| and |level sep|."
+
+example
+[[
+\tikz \graph [CircularLayout, layer sep=1cm] {
+ a -- b -- c -- a -- d -- e -- f -- g -- d;
+ b -- {x,y,z};
+};
+]]
+--------------------------------------------------------------------------------
+
+
+--------------------------------------------------------------------------------
+key "CircularLayout.minDistSibling"
+summary "The minimal padding between sibling nodes."
+
+documentation "This is an alias for |sibling sep|."
+
+example
+[[
+\tikz \graph [CircularLayout, sibling sep=1cm] {
+ a -- b -- c -- a -- d -- e -- f -- g -- d;
+ b -- {x,y,z};
+};
+]]
+--------------------------------------------------------------------------------
+
+
+-- Local Variables:
+-- mode:latex
+-- End: \ No newline at end of file
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/module/AcyclicSubgraphModule.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/module/AcyclicSubgraphModule.lua
new file mode 100644
index 0000000000..b2bb7e1d24
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/module/AcyclicSubgraphModule.lua
@@ -0,0 +1,18 @@
+-- Copyright 2013 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$
+
+
+---
+-- @section subsubsection {Computing Acyclic Subgraphs Module}
+--
+
+local _
+
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/module/HierarchyLayoutModule.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/module/HierarchyLayoutModule.lua
new file mode 100644
index 0000000000..a0292530cd
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/module/HierarchyLayoutModule.lua
@@ -0,0 +1,17 @@
+-- Copyright 2013 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$
+
+
+---
+-- @section subsubsection {The Hierarchy Layout Module}
+--
+
+local _
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/module/InitialPlacer.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/module/InitialPlacer.lua
new file mode 100644
index 0000000000..328a2806db
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/module/InitialPlacer.lua
@@ -0,0 +1,17 @@
+-- Copyright 2013 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$
+
+
+---
+-- @section subsubsection {The Initial Placer Module}
+--
+
+local _
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/module/MultilevelBuilder.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/module/MultilevelBuilder.lua
new file mode 100644
index 0000000000..9604c98640
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/module/MultilevelBuilder.lua
@@ -0,0 +1,17 @@
+-- Copyright 2013 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$
+
+
+---
+-- @section subsubsection {The Multilevel Builder Module}
+--
+
+local _
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/module/RankingModule.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/module/RankingModule.lua
new file mode 100644
index 0000000000..8dce27a335
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/module/RankingModule.lua
@@ -0,0 +1,17 @@
+-- Copyright 2013 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$
+
+
+---
+-- @section subsubsection {The Ranking Module}
+--
+
+local _
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/module/TwoLayerCrossMin.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/module/TwoLayerCrossMin.lua
new file mode 100644
index 0000000000..877cba47f6
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/module/TwoLayerCrossMin.lua
@@ -0,0 +1,17 @@
+-- Copyright 2013 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$
+
+
+---
+-- @section subsubsection {The Two Layer Crossing Minimization Module}
+--
+
+local _
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/planarity.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/planarity.lua
new file mode 100644
index 0000000000..fb1abb5ccd
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/planarity.lua
@@ -0,0 +1,19 @@
+-- Copyright 2013 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$
+
+
+
+---
+-- @section subsection {Algorithms for Drawing Planar Graphs}
+--
+--
+
+local _ \ No newline at end of file
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/planarity/PlanarizationLayout.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/planarity/PlanarizationLayout.lua
new file mode 100644
index 0000000000..e61f9281b9
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/doc/ogdf/planarity/PlanarizationLayout.lua
@@ -0,0 +1,66 @@
+-- Copyright 2013 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$
+
+
+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 "PlanarizationLayout"
+summary "The planarization layout algorithm."
+
+documentation
+[[
+ A |PlanarizationLayout| represents a customizable implementation
+ of the planarization approach for drawing graphs. The implementation
+ used in PlanarizationLayout is based on the following publication:
+ %
+ \begin{itemize}
+ \item C. Gutwenger, P. Mutzel: \emph{An Experimental Study of Crossing
+ Minimization Heuristics.} 11th International Symposium on Graph
+ Drawing 2003, Perugia (GD '03), LNCS 2912, pp. 13--24, 2004.
+ \end{itemize}
+]]
+
+example
+[[
+\tikz \graph [PlanarizationLayout] { a -- {b,c,d,e,f} -- g -- a };
+]]
+--------------------------------------------------------------------------------
+
+
+
+--------------------------------------------------------------------------------
+key "PlanarizationLayout.preprocessCliques"
+summary "Configures, whether cliques are collapsed in a preprocessing step."
+documentation
+[[
+ If set to true, a preprocessing for cliques (complete subgraphs)
+ is performed and cliques will be laid out in a special form (straight-line,
+ not orthogonal). The preprocessing may reduce running time and improve
+ layout quality if the input graphs contains dense subgraphs.
+]]
+--------------------------------------------------------------------------------
+
+
+
+--------------------------------------------------------------------------------
+key "PlanarizationLayout.minCliqueSize"
+summary "The minimum size of cliques collapsed in preprocessing."
+--------------------------------------------------------------------------------
+
+
+-- Local Variables:
+-- mode:latex
+-- End: \ No newline at end of file
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/examples.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/examples.lua
new file mode 100644
index 0000000000..de33c0ed37
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/examples.lua
@@ -0,0 +1,26 @@
+-- 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$
+
+
+
+-- Imports
+
+require "pgf"
+require "pgf.gd"
+
+
+-- Declare namespace
+pgf.gd.examples = {}
+
+
+-- Done
+
+return pgf.gd.examples \ No newline at end of file
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/examples/ASCIIDisplayer.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/examples/ASCIIDisplayer.lua
new file mode 100644
index 0000000000..5acc28396b
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/examples/ASCIIDisplayer.lua
@@ -0,0 +1,32 @@
+local InterfaceToDisplay = require "pgf.gd.interface.InterfaceToDisplay"
+
+InterfaceToDisplay.bind(require "pgf.gd.examples.BindingToASCII")
+require "pgf.gd.layered.library"
+require "pgf.gd.force.library"
+
+local algorithm = io.read():match("%s*graph%s*%[(.-)%]")
+
+InterfaceToDisplay.pushPhase(algorithm, "main", 1)
+InterfaceToDisplay.pushOption("level distance", 6, 2)
+InterfaceToDisplay.pushOption("sibling distance", 8, 3)
+InterfaceToDisplay.beginGraphDrawingScope(3)
+InterfaceToDisplay.pushLayout(4)
+
+for line in io.lines() do
+ if line:match("}") then
+ break
+ elseif line:find("-") then
+ local n1, dir, n2 = string.match(line, "^%s*(.-)%s*(-.)%s*(.-)%s*;")
+ InterfaceToDisplay.createEdge(n1, n2, dir, 4)
+ else
+ local n1 = string.match(line, "^%s*(.-)%s*;")
+ InterfaceToDisplay.createVertex(n1, "rectangle", nil, 4)
+ end
+end
+
+InterfaceToDisplay.runGraphDrawingAlgorithm()
+InterfaceToDisplay.renderGraph()
+InterfaceToDisplay.endGraphDrawingScope()
+
+
+
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/examples/BindingToASCII.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/examples/BindingToASCII.lua
new file mode 100644
index 0000000000..2b5c9cc0b6
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/examples/BindingToASCII.lua
@@ -0,0 +1,87 @@
+local lib = require "pgf.gd.lib"
+
+-- Create a binding to ourselves
+local BindingToASCII = lib.class { base_class = require "pgf.gd.bindings.Binding" }
+
+local canvas
+
+function BindingToASCII:renderStart()
+ canvas = {}
+ -- Clear the canvas
+ for x=-30,30 do
+ canvas [x] = {}
+ for y=-30,30 do
+ canvas[x][y] = ' '
+ end
+ end
+end
+
+function BindingToASCII:renderStop()
+ for y=10,-30,-1 do
+ local t = {}
+ for x=-30,30 do
+ local s = canvas[x][y]
+ for i=1,#s do
+ pos = x+30+i-math.floor(#s/2)
+ if not t[pos] or t[pos] == " " or t[pos] == "." then
+ t[pos] = string.sub(s,i,i)
+ end
+ end
+ end
+ print(table.concat(t))
+ end
+end
+
+function BindingToASCII:renderVertex(v)
+ canvas [math.floor(v.pos.x)][math.floor(v.pos.y)] = v.name
+end
+
+function BindingToASCII:renderEdge(e)
+
+ local function connect (p,q)
+
+ local x1, y1, x2, y2 = math.floor(p.x+0.5), math.floor(p.y+0.5), math.floor(q.x+0.5), math.floor(q.y+0.5)
+
+ -- Go upward with respect to x
+ if x2 < x1 then
+ x1, y1, x2, y2 = x2, y2, x1, y1
+ end
+
+ local delta_x = x2-x1
+ local delta_y = y2-y1
+
+ if math.abs(delta_x) > math.abs(delta_y) then
+ local slope = delta_y/delta_x
+ for i=x1,x2 do
+ local x,y = i, math.floor(y1 + (i-x1)*slope + 0.5)
+
+ if canvas[x][y] == " " then
+ canvas[x][y] = '.'
+ end
+ end
+ elseif math.abs(delta_y) > 0 then
+ local slope = delta_x/delta_y
+ for i=y1,y2,(y1<y2 and 1) or -1 do
+ local x,y = math.floor(x1 + (i-y1)*slope + 0.5), i
+
+ if canvas[x][y] == " " then
+ canvas[x][y] = '.'
+ end
+ end
+ end
+ end
+
+
+ local p = e.tail.pos
+
+ for i=1,#e.path do
+ if type(e.path[i]) == "table" then
+ connect(p, e.path[i])
+ p = e.path[i]
+ end
+ end
+
+ connect(p, e.head.pos)
+end
+
+return BindingToASCII
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/examples/SimpleDemo.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/examples/SimpleDemo.lua
new file mode 100644
index 0000000000..46eb4eff38
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/examples/SimpleDemo.lua
@@ -0,0 +1,82 @@
+-- Copyright 2010 by Renée Ahrens, Olof Frahm, Jens Kluttig, Matthias Schulz, Stephan Schuster
+-- 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$
+
+
+---
+-- @section subsubsection {The ``Hello World'' of Graph Drawing}
+--
+-- @end
+
+
+-- Inputs
+local declare = require "pgf.gd.interface.InterfaceToAlgorithms".declare
+
+---
+
+declare {
+ key = "simple demo layout",
+ algorithm = {
+ run =
+ function (self)
+ local g = self.digraph
+ local alpha = (2 * math.pi) / #g.vertices
+
+ for i,vertex in ipairs(g.vertices) do
+ local radius = vertex.options['radius'] or g.options['radius']
+ vertex.pos.x = radius * math.cos(i * alpha)
+ vertex.pos.y = radius * math.sin(i * alpha)
+ end
+ end
+ },
+
+ summary = [["
+ This algorithm is the ``Hello World'' of graph drawing.
+ "]],
+ documentation = [=["
+ The algorithm arranges nodes in a circle (without paying heed to the
+ sizes of the nodes or to the edges). In order to ``really'' layout
+ nodes in a circle, use |simple necklace layout|; the present layout
+ is only intended to demonstrate how much (or little) is needed to
+ implement a graph drawing algorithm.
+ %
+\begin{codeexample}[code only, tikz syntax=false]
+-- File pgf.gd.examples.SimpleDemo
+local declare = require "pgf.gd.interface.InterfaceToAlgorithms".declare
+
+declare {
+ key = "simple demo layout",
+ algorithm = {
+ run =
+ function (self)
+ local g = self.digraph
+ local alpha = (2 * math.pi) / #g.vertices
+
+ for i,vertex in ipairs(g.vertices) do
+ local radius = vertex.options['radius'] or g.options['radius']
+ vertex.pos.x = radius * math.cos(i * alpha)
+ vertex.pos.y = radius * math.sin(i * alpha)
+ end
+ end
+ },
+ summary = "This algorithm is the 'Hello World' of graph drawing.",
+ documentation = [["
+ This algorithm arranges nodes in a circle ...
+ "]]
+}
+\end{codeexample}
+
+ On the display layer (\tikzname, that is) the algorithm can now
+ immediately be employed; you just need to say
+ |\usegdlibrary{examples.SimpleDemo}| at the beginning
+ somewhere.
+ "]=]
+} \ No newline at end of file
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/examples/SimpleEdgeDemo.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/examples/SimpleEdgeDemo.lua
new file mode 100644
index 0000000000..3ff5f4ea23
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/examples/SimpleEdgeDemo.lua
@@ -0,0 +1,144 @@
+-- Copyright 2010 by Renée Ahrens, Olof Frahm, Jens Kluttig, Matthias Schulz, Stephan Schuster
+-- 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$
+
+
+---
+-- @section subsubsection {How To Generate Edges Inside an Algorithm}
+--
+-- @end
+
+
+-- Imports
+local InterfaceToAlgorithms = require "pgf.gd.interface.InterfaceToAlgorithms"
+local declare = InterfaceToAlgorithms.declare
+
+-- The class object
+local SimpleEdgeDemo = {}
+
+
+---
+declare {
+ key = "simple edge demo layout",
+ algorithm = SimpleEdgeDemo,
+
+ summary = "This algorithm shows how edges can be created by an algorithm.",
+ documentation = [["
+ For its job, the algorithm uses the function |createEdge|, which can be
+ called during the run of the algorithm to create edges in the
+ syntactic graph. The algorithm first does exactly the same as the
+ simple demo layout, then it creates an edge for every node where the
+ |new edge to| option is set. You will see in the code how this
+ option is declared and how we use it to look up a vertex in the
+ graph by its name.
+ %
+\begin{codeexample}[preamble={ \usetikzlibrary{graphs,graphdrawing}
+ \usegdlibrary{examples}}]
+\tikz [simple edge demo layout]
+\graph [radius=2cm] {
+ a, b, c, d, e, f;
+
+ e -> [red] f; % normal edge
+
+ % Edges generated by the algorithm:
+ a[new edge to=b];
+ b[new edge to=d];
+ c[new edge to=f];
+};
+\end{codeexample}
+
+ And the algorithm:
+ %
+\begin{codeexample}[code only, tikz syntax=false]
+ -- File pgf.gd.examples.SimpleEdgeDemo
+
+ -- Imports
+ local InterfaceToAlgorithms = require "pgf.gd.interface.InterfaceToAlgorithms"
+ local declare = InterfaceToAlgorithms.declare
+
+ -- The class object
+ local SimpleEdgeDemo = {}
+
+declare {
+ key = "simple edge demo layout",
+ algorithm = SimpleEdgeDemo,
+ summary = "This algorithm shows...",
+}
+\end{codeexample}
+
+ Next comes the declaration of the new option |new edge to|:
+ %
+\begin{codeexample}[code only, tikz syntax=false]
+declare {
+ key = "new edge to",
+ type = "string",
+ summary = "This option takes the name of a vertex..."
+}
+\end{codeexample}
+
+ Finally, the algorithm's code:
+ %
+\begin{codeexample}[code only, tikz syntax=false]
+function SimpleEdgeDemo:run()
+ -- As in a SimpleDemo:
+ ...
+ -- Now add some edges:
+ for _,tail in ipairs(g.vertices) do
+ local name = tail.options['new edge to']
+ if name then
+ local node = InterfaceToAlgorithms.findVertexByName(name)
+ if node and self.digraph:contains(node) then
+ InterfaceToAlgorithms.createEdge (self, tail, node)
+ end
+ end
+ end
+end
+\end{codeexample}
+ "]]
+}
+
+---
+declare {
+ key = "new edge to",
+ type = "string",
+
+ summary = [["
+ This option takes the name of a vertex. An edge leading to this
+ vertex is added to the syntactic digraph.
+ "]]
+}
+
+
+function SimpleEdgeDemo:run()
+
+ -- As in a SimpleDemo:
+ local g = self.digraph
+ local alpha = (2 * math.pi) / #g.vertices
+
+ for i,vertex in ipairs(g.vertices) do
+ local radius = vertex.options['radius'] or g.options['radius']
+ vertex.pos.x = radius * math.cos(i * alpha)
+ vertex.pos.y = radius * math.sin(i * alpha)
+ end
+
+ -- Now add some edges:
+ for _,tail in ipairs(g.vertices) do
+ local name = tail.options['new edge to']
+ if name then
+ local node = InterfaceToAlgorithms.findVertexByName(name)
+ if node and self.digraph:contains(node) then
+ InterfaceToAlgorithms.createEdge (self, tail, node)
+ end
+ end
+ end
+end
+
+return SimpleEdgeDemo
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/examples/SimpleHuffman.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/examples/SimpleHuffman.lua
new file mode 100644
index 0000000000..6aa9bffb72
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/examples/SimpleHuffman.lua
@@ -0,0 +1,287 @@
+-- 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$
+
+
+---
+-- @section subsubsection {How To Generate Nodes Inside an Algorithm}
+--
+-- @end
+
+
+
+-- Imports
+local layered = require "pgf.gd.layered"
+local InterfaceToAlgorithms = require "pgf.gd.interface.InterfaceToAlgorithms"
+local declare = require "pgf.gd.interface.InterfaceToAlgorithms".declare
+
+-- The class
+local SimpleHuffman = {}
+
+
+---
+
+declare {
+ key = "simple Huffman layout",
+ algorithm = SimpleHuffman,
+
+ postconditions = {
+ upward_oriented = true
+ },
+
+ summary = [["
+ This algorithm demonstrates how an algorithm can generate new nodes.
+ "]],
+ documentation = [["
+ The input graph should just consist of some nodes (without
+ edges) and each node should have a |probability| key set. The nodes
+ will then be arranged in a line (as siblings) and a Huffman tree
+ will be constructed ``above'' these nodes. For the construction of
+ the Huffman tree, new nodes are created and connected.
+
+ \pgfgdset{
+ HuffmanLabel/.style={/tikz/edge node={node[fill=white,font=\footnotesize,inner sep=1pt]{#1}}},
+ HuffmanNode/.style={/tikz/.cd,circle,inner sep=0pt,outer sep=0pt,draw,minimum size=3pt}
+ }
+
+\begin{codeexample}[preamble={ \usetikzlibrary{graphs,graphdrawing,quotes}
+ \usegdlibrary{examples}}]
+\tikz \graph [simple Huffman layout,
+ level distance=7mm, sibling distance=8mm, grow'=up]
+{
+ a ["0.5", probability=0.5],
+ b ["0.12", probability=0.12],
+ c ["0.2", probability=0.2],
+ d ["0.1", probability=0.1],
+ e ["0.11", probability=0.11]
+};
+\end{codeexample}
+ %
+ The file starts with some setups and declarations:
+ %
+\begin{codeexample}[code only, tikz syntax=false]
+-- File pgf.gd.examples.SimpleHuffman
+
+local declare = require "pgf.gd.interface.InterfaceToAlgorithms".declare
+
+-- The class
+local SimpleHuffman = {}
+
+declare {
+ key = "simple Huffman layout",
+ algorithm = SimpleHuffman,
+ postconditions = { upward_oriented = true }
+ summary = "..."
+}
+
+declare {
+ key = "probability",
+ type = "number",
+ initial = "1",
+ summary = "..."
+}
+
+-- Import
+local layered = require "pgf.gd.layered"
+local InterfaceToAlgorithms = require "pgf.gd.interface.InterfaceToAlgorithms"
+local Storage = require "pgf.gd.lib.Storage"
+
+local probability = Storage.new()
+local layer = Storage.new()
+
+function SimpleHuffman:run()
+ -- Construct a Huffman tree on top of the vertices...
+\end{codeexample}
+
+ Next comes a setup, where we create the working list of vertices
+ that changes as the Huffman coding method proceeds:
+ %
+\begin{codeexample}[code only, tikz syntax=false]
+ -- Shorthand
+ local function prop (v)
+ return probability[v] or v.options['probability']
+ end
+
+ -- Copy the vertex table, since we are going to modify it:
+ local vertices = {}
+ for i,v in ipairs(self.ugraph.vertices) do
+ vertices[i] = v
+ end
+\end{codeexample}
+
+ The initial vertices are arranged in a line on the last layer. The
+ function |ideal_sibling_distance| takes care of the rather
+ complicated handling of the (possibly rotated) bounding boxes and
+ separations. The |props| and |layer| are tables used by
+ algorithms to ``store stuff'' at a vertex or at an arc. The
+ table will be accessed by |arrange_layers_by_baselines| to
+ determine the ideal vertical placements.
+ %
+\begin{codeexample}[code only, tikz syntax=false]
+ -- Now, arrange the nodes in a line:
+ vertices [1].pos.x = 0
+ layer[ vertices [1] ] = #vertices
+ for i=2,#vertices do
+ local d = layered.ideal_sibling_distance(self.adjusted_bb, self.ugraph, vertices[i-1], vertices[i])
+ vertices [i].pos.x = vertices[i-1].pos.x + d
+ layer[ vertices [i] ] = #vertices
+ end
+\end{codeexample}
+
+ Now comes the actual Huffman algorithm: Always find the vertices
+ with a minimal probability\dots
+ %
+\begin{codeexample}[code only, tikz syntax=false]
+ -- Now, do the Huffman thing...
+ while #vertices > 1 do
+ -- Find two minimum probabilities
+ local min1, min2
+
+ for i=1,#vertices do
+ if not min1 or prop(vertices[i]) < prop(vertices[min1]) then
+ min2 = min1
+ min1 = i
+ elseif not min2 or prop(vertices[i]) < prop(vertices[min2]) then
+ min2 = i
+ end
+ end
+\end{codeexample}
+ %
+ \dots and connect them with a new node. This new node gets the
+ option |HuffmanNode|. It is now the job of the higher layers to map
+ this option to something ``nice''.
+ %
+\begin{codeexample}[code only, tikz syntax=false]
+ -- Create new node:
+ local p = prop(vertices[min1]) + prop(vertices[min2])
+ local v = InterfaceToAlgorithms.createVertex(self, { generated_options = {{key="HuffmanNode"}}})
+ probability[v] = p
+ layer[v] = #vertices-1
+ v.pos.x = (vertices[min1].pos.x + vertices[min2].pos.x)/2
+ vertices[#vertices + 1] = v
+
+ InterfaceToAlgorithms.createEdge (self, v, vertices[min1],
+ {generated_options = {{key="HuffmanLabel", value = "0"}}})
+ InterfaceToAlgorithms.createEdge (self, v, vertices[min2],
+ {generated_options = {{key="HuffmanLabel", value = "1"}}})
+
+ table.remove(vertices, math.max(min1, min2))
+ table.remove(vertices, math.min(min1, min2))
+ end
+\end{codeexample}
+ %
+ Ok, we are mainly done now. Finish by computing vertical placements
+ and do formal cleanup.
+ %
+\begin{codeexample}[code only, tikz syntax=false]
+ layered.arrange_layers_by_baselines(layers, self.adjusted_bb, self.ugraph)
+end
+\end{codeexample}
+
+ In order to use the class, we have to make sure that, on the
+ display layer, the options |HuffmanLabel| and |HuffmanNode| are
+ defined. This is done by adding, for instance, the following to
+ \tikzname:
+ %
+\begin{codeexample}[code only]
+\pgfkeys{
+ /graph drawing/HuffmanLabel/.style={
+ /tikz/edge node={node[fill=white,font=\footnotesize,inner sep=1pt]{#1}}
+ },
+ /graph drawing/HuffmanNode/.style={
+ /tikz/.cd,circle,inner sep=0pt,outer sep=0pt,draw,minimum size=3pt
+ }
+}
+\end{codeexample}
+ "]]
+}
+
+
+---
+
+declare {
+ key = "probability",
+ type = "number",
+ initial = "1",
+
+ summary = [["
+ The probability parameter. It is used by the Huffman algorithm to
+ group nodes.
+ "]]
+}
+
+-- Imports
+
+local Storage = require 'pgf.gd.lib.Storage'
+
+-- Storages
+
+local probability = Storage.new()
+local layer = Storage.new()
+
+
+function SimpleHuffman:run()
+ -- Construct a Huffman tree on top of the vertices...
+
+ -- Shorthand
+ local function prop (v)
+ return probability[v] or v.options['probability']
+ end
+
+ -- Copy the vertex table, since we are going to modify it:
+ local vertices = {}
+ for i,v in ipairs(self.ugraph.vertices) do
+ vertices[i] = v
+ end
+
+ -- Now, arrange the nodes in a line:
+ vertices [1].pos.x = 0
+ layer[vertices [1]] = #vertices
+ for i=2,#vertices do
+ local d = layered.ideal_sibling_distance(self.adjusted_bb, self.ugraph, vertices[i-1], vertices[i])
+ vertices [i].pos.x = vertices[i-1].pos.x + d
+ layer[vertices [i]] = #vertices
+ end
+
+ -- Now, do the Huffman thing...
+ while #vertices > 1 do
+ -- Find two minimum probabilities
+ local min1, min2
+
+ for i=1,#vertices do
+ if not min1 or prop(vertices[i]) < prop(vertices[min1]) then
+ min2 = min1
+ min1 = i
+ elseif not min2 or prop(vertices[i]) < prop(vertices[min2]) then
+ min2 = i
+ end
+ end
+
+ -- Create new node:
+ local p = prop(vertices[min1]) + prop(vertices[min2])
+ local v = InterfaceToAlgorithms.createVertex(self, { generated_options = {{key="HuffmanNode"}}})
+ probability[v] = p
+ layer[v] = #vertices-1
+ v.pos.x = (vertices[min1].pos.x + vertices[min2].pos.x)/2
+ vertices[#vertices + 1] = v
+
+ InterfaceToAlgorithms.createEdge (self, v, vertices[min1],
+ {generated_options = {{key="HuffmanLabel", value = "0"}}})
+ InterfaceToAlgorithms.createEdge (self, v, vertices[min2],
+ {generated_options = {{key="HuffmanLabel", value = "1"}}})
+
+ table.remove(vertices, math.max(min1, min2))
+ table.remove(vertices, math.min(min1, min2))
+ end
+
+ layered.arrange_layers_by_baselines(layer, self.adjusted_bb, self.ugraph)
+end
+
+return SimpleHuffman
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/examples/example_graph_for_ascii_displayer.txt b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/examples/example_graph_for_ascii_displayer.txt
new file mode 100644
index 0000000000..052ebdcdb8
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/examples/example_graph_for_ascii_displayer.txt
@@ -0,0 +1,20 @@
+graph [layered layout] {
+ Alice;
+ Bob;
+ Charly;
+ Dave;
+ Eve;
+ Fritz;
+ George;
+ Alice -> Bob;
+ Alice -> Charly;
+ Charly -> Dave;
+ Bob -> Dave;
+ Dave -> Eve;
+ Eve -> Fritz;
+ Fritz -> Alice;
+ George -> Eve;
+ George -> Fritz;
+ Alice -> George;
+}
+
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/examples/library.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/examples/library.lua
new file mode 100644
index 0000000000..0c977600a7
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/examples/library.lua
@@ -0,0 +1,30 @@
+-- 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$
+
+
+
+---
+-- This package presents some examples of how different aspects of the
+-- graph drawing engine can be used. In particular, the algorithms of
+-- this package are not really meant to be used to layout graphs
+-- (although they can be used, in principle); rather you are invited
+-- to have a look at their implementation and to adapt them to your needs.
+--
+-- @library
+
+local examples
+
+
+-- Load algorithms from:
+require "pgf.gd.examples.SimpleDemo"
+require "pgf.gd.examples.SimpleEdgeDemo"
+require "pgf.gd.examples.SimpleHuffman"
+
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/experimental/evolving/GraphAnimationCoordination.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/experimental/evolving/GraphAnimationCoordination.lua
new file mode 100644
index 0000000000..31f8f27431
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/experimental/evolving/GraphAnimationCoordination.lua
@@ -0,0 +1,638 @@
+-- Copyright 2015 by Malte Skambath
+--
+-- 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
+
+
+--
+--
+-- @field.visible_objects An array which stores for each supernode a mapping
+-- of snapshots to the related visible snapshot nodes.
+-- Note that these mappings may differ from the supergraph
+-- because if there are two snapshot nodes in consecutive snapshots
+-- then the first can be shown for a longer time period to
+-- put aside some fade animations.
+-- @field is_first A table storing for each snapshot node or snapshot arc if it
+-- appears in its snapshot. This means that in the previous snapshot
+-- there is no corresponding arc or node.
+-- @field is_last A table storing for each snapshot node or arc if there
+-- is no representative in the next snapshot.
+-- @field move_on_enter A table which stores for each snapshot object if it is in
+-- motion while it appears in its snapshot.
+-- @field move_on_leave A table which stores for each snapshot object if it is in
+-- motion while switching to the next snapshot
+-- @field last_rep
+-- A table which stores for every snapshot node if the representing (visible) node
+-- disappears with the next snapshot.
+--
+-- @field previous_node The same as |next_node| just for the previous node
+-- @field next_node A Storage to map each snapshot node to the next node in the
+-- following snapshot related to the same supernode.
+-- If in the next snapshot there is node following snapshot node
+-- then the value is nil.
+--
+local GraphAnimationCoordination = {}
+
+-- Imports
+local lib = require "pgf.gd.lib"
+local declare = require("pgf.gd.interface.InterfaceToAlgorithms").declare
+local Storage = require "pgf.gd.lib.Storage"
+local Coordinate = require "pgf.gd.model.Coordinate"
+
+
+declare {
+ key = "modified",
+ type = "boolean",
+ Initial = false,
+ documentation = [["
+ This key specifies, if a supernode changed its
+ visual properties since the last snapshot.
+ The default value is |false| and prevent the algorithm
+ to produce a lot of unnecessary animations.
+ "]]
+}
+
+declare {
+ key = "unmodified",
+ use = {
+ { key = "modified", boolean = false},
+ },
+}
+
+
+---
+declare {
+ key = "minimum rest time",
+ type = "number",
+ initial = 0.5,
+ documentation = [["
+ This key specifies a minimum time in which a single node
+ has to be prohibited to be animated.
+ For a node with minimum rest time of 1s that exists in a snapshot
+ at time $t$ this means that all animations including movements and fadings
+ are only allowed before $t$-0.5s and after $t$+0.5s.
+ "]],
+}
+
+declare {
+ key = "maximum motion time",
+ type = "number",
+ initial = math.huge,
+ documentation = [["
+ Use this key if you want to limit the time during nodes are allowed to move
+ when they changing their positions.
+ "]],
+}
+
+declare {
+ key = "overlapping transition",
+ type = "boolean",
+ initial = true,
+ documentation = [["
+ Use this key if you want to allow that the fade animations for or
+ disappearing nodes may occurs while the mid time between two snapshots.
+ If false then the appearing ends on the midtime and the disappearing
+ starts in this moment.
+ "]]
+}
+
+---
+declare {
+ key = "default evolving graph animation",
+ algorithm = GraphAnimationCoordination,
+ phase = "evolving graph animation",
+ phase_default = true,
+ summary = [["
+ This phase animates all vertices including movements and
+ fade in or fade out animations given an evolving graph as sequence
+ of single snapshot graphs.
+ "]],
+ documentation = [["
+ This phase animates all vertices including movements and
+ fade in or fade out animations given an evolving graph as sequence
+ of single snapshot graphs.
+
+ Your algorithm needs to work on evolving graphs and has to use
+ the |evolving graph animation| phase. You do not need to use
+ this key by yourself then because this key starts the default
+ algorithm algorithm of the phase.
+ %
+ \begin{codeexample}[]
+ local ga_class = self.digraph.options.algorithm_phases['evolving graph animation']
+ -- animate graph
+ ga_class.new {
+ main_algorithm = self,
+ supergraph = supergraph,
+ digraph = self.digraph,
+ ugraph = self.ugraph
+ }:run()
+ \end{codeexample}
+
+ This algorithm and phase require a supergraph instance and the original
+ digraph and ugraph. Note that you have to set the layout of the snapshot
+ nodes before running algorithms of this is useful.
+ "]],
+}
+
+-- Help functions
+
+--
+-- Appends a move animation to a given snapshot object such that the
+-- object moves from one point to another on a straight line. Note
+-- that the coordinates of the two points are given as relative
+-- coordinates to the current origin of the object.
+--
+-- This means if we want to move a node 1cm to the right the value of
+-- |c_from| has to be (0,0) while |c_to| must be (1,0). The argument
+-- |c_from| is useful for a node which has a position but its
+-- previous node related to the same supervertex is at a different
+-- position. Then we can use this argument to move the new node to
+-- its origin position for smooth transitions.
+--
+-- @field object The snapshot object which should be moved
+--
+-- @field c_from The coordinate where the animation starts
+--
+-- @field c_to The coordinate where the animation should end
+--
+-- @field t_start The time when the movement starts.
+--
+-- @field t_end The time when the animation stops.
+local function append_move_animation(object, c_from, c_to, t_start, t_end)
+ if not object then return end
+ assert(object, "no object to animate")
+ if ((c_from.x~=c_to.x) or (c_from.y~=c_to.y))then
+ local animations = object.animations or {}
+ local c1 = Coordinate.new((2*c_from.x+c_to.x)/3,(2*c_from.y+c_to.y)/3)
+ local c2 = Coordinate.new((c_from.x+2*c_to.x)/3,(c_from.y+2*c_to.y)/3)
+ local t1 = (7*t_start + 5*t_end)/12
+ local t2 = (5*t_start + 7*t_end)/12
+ table.insert(animations, {
+ attribute = "translate",
+ entries = {
+ { t = t_start, value = c_from},
+-- { t = t1, value = c1 },
+-- { t = t2, value = c2 },
+ { t = t_end, value = c_to }
+ },
+ options = { { key = "freeze at end", },
+-- {key = "entry control", value="0}{1",}
+ }
+ })
+ object.animations = animations
+ end
+end
+
+local function append_fade_animation(object, v_start, v_end, t_start, t_end)
+ local animations = object.animations or {}
+
+ if v_start == 0 then
+ table.insert(animations, {
+ attribute = "stage",
+ entries = { { t = t_start, value = "true"}, },
+ options = { { key = "freeze at end" } }
+ })
+ elseif v_end == 0 and nil then
+ table.insert(animations, {
+ attribute = "stage",
+ entries = { { t = t_end, value = "false"}, },
+ options = { --{ key = "freeze at end" }
+ }
+ })
+ end
+
+ table.insert(animations, {
+ attribute = "opacity",
+ entries = {
+ { t = t_start, value = v_start },
+ { t = t_end, value = v_end } },
+ options = { { key = "freeze at end" } }
+ })
+ object.animations = animations
+end
+
+--
+-- check if the difference/vector between two pairs (a1,a2),(b1,b2) of points
+-- is the same.
+local function eq_offset(a1, a2, b1, b2)
+ local dx = ((a1.x-a2.x) - (b1.x-b2.x))
+ local dy = ((a1.y-a2.y) - (b1.y-b2.y))
+ if dx<0 then dx = -dx end
+ if dy<0 then dy = -dy end
+ return dx<0.001 and dy<0.001
+end
+
+--
+-- Check if two arcs connect a pair of nodes at the same position.
+-- This can be used as an indicator that two consecutive arcs
+-- can be represented by the same arc object.
+--
+local function eq_arc(arc1, arc2)
+ if not arc1 or not arc2 then
+ return false
+ end
+ return eq_offset(arc1.tail.pos, arc1.head.pos, arc2.tail.pos, arc2.head.pos)
+end
+
+
+-- Implementation
+
+function GraphAnimationCoordination:run()
+ assert(self.supergraph, "no supergraph defined")
+
+ self.is_first = Storage.new()
+ self.is_last = Storage.new()
+ self.last_rep = Storage.new()
+ self.move_on_enter = Storage.new()
+ self.move_on_leave = Storage.new()
+ self.previous_node = Storage.new()
+ self.next_node = Storage.new()
+ self.visible_objects = Storage.new()
+
+
+ self:precomputeNodes()
+ self:precomputeEdges()
+ self:animateNodeAppearing()
+ self:animateEdgeAppearing()
+ self:generateNodeMotions()
+ self:generateEdgeMotions()
+end
+
+function GraphAnimationCoordination:generateNodeMotions(node_types)
+ local supergraph = self.supergraph
+ local graph = self.digraph
+
+ for _, supervertex in ipairs(self.supergraph.vertices) do
+ local lj = -1
+ local last_v = nil
+ local last_time = nil
+ for j, s in ipairs(supergraph.snapshots) do
+ local vertex = supergraph:getSnapshotVertex(supervertex, s)
+
+ if lj == j-1 and vertex and last_v then
+ local mrt1 = last_v.options["minimum rest time"]/2
+ local mrt2 = vertex.options["minimum rest time"]/2
+
+ local s1 = Coordinate.new(0,0)
+ local e1 = Coordinate.new(vertex.pos.x-last_v.pos.x,-vertex.pos.y+last_v.pos.y)
+
+ local s2 = Coordinate.new(-vertex.pos.x+last_v.pos.x,vertex.pos.y-last_v.pos.y)
+ local e2 = Coordinate.new(0,0)
+
+ local t_end = s.timestamp - math.max(0, mrt2)
+ local t_start = last_time + math.max(0,mrt1)
+
+ local representative = self.visible_objects[supervertex][s]
+ if representative == vertex then
+ append_move_animation(vertex, s2, e2, t_start, t_end)
+ append_move_animation(last_v, s1, e1, t_start, t_end)
+ else
+ append_move_animation(representative,s1,e1,t_start,t_end)
+ end
+ end
+ last_time = s.timestamp
+ lj = j
+ last_v = vertex
+ end
+ end
+end
+
+
+
+
+
+function GraphAnimationCoordination:generateEdgeMotions()
+ local supergraph = self.supergraph
+ local graph = self.digraph
+
+ for i, arc in ipairs(supergraph.arcs) do
+ local head = arc.head
+ local tail = arc.tail
+
+ local last_arc = nil
+ local last_time = nil
+ local last_v = nil
+ local last_w = nil
+
+ for j, s in ipairs(supergraph.snapshots) do
+ local v = supergraph:getSnapshotVertex(tail,s)
+ local w = supergraph:getSnapshotVertex(head,s)
+
+ if v and w then
+ local this_arc = graph:arc(v,w) --or graph:arc(w,v)
+ if this_arc then
+ if this_arc and last_arc then
+ local mrt1 = last_v.options["minimum rest time"]/2
+ local mrt2 = v.options["minimum rest time"]/2
+
+ local s1 = Coordinate.new(0,0)--lv.pos
+ local e1 = Coordinate.new(v.pos.x-last_v.pos.x,-v.pos.y+last_v.pos.y)
+
+ local s2 = Coordinate.new(-v.pos.x+last_v.pos.x,v.pos.y-last_v.pos.y)
+ local e2 = Coordinate.new(0,0)
+
+ local t_end = s.timestamp - math.max(0,mrt2)
+ local t_start = last_time + math.max(0,mrt1)
+
+ local representative = self.visible_objects[arc][s]
+ if representative == this_arc then
+ append_move_animation(last_arc, s1, e1, t_start,t_end)
+ append_move_animation(this_arc, s2, e2, t_start,t_end)
+ else
+ append_move_animation(representative,s1,e1,t_start,t_end)
+ end
+ this_arc = representative
+ end
+ last_arc = this_arc
+ last_v = v
+ last_time = s.timestamp
+ else
+ last_arc = nil
+ end
+ else
+ last_arc = nil
+ end
+ end
+ end
+end
+
+--
+--
+-- @field t_transition The mid time between two snapshot times.
+-- @field fade_duration The duration of the fade animation
+-- @field overlapping A boolean defining if the animation occurs
+-- before and after the mid time (true) or if it
+-- starts/end only in one interval (false)
+-- @field closing A boolean specifying if this is an outfading time
+local function compute_fade_times(t_transition, fade_duration, overlapping, closing)
+
+ if overlapping then
+ t_start = t_transition - fade_duration / 2
+ t_end = t_transition + fade_duration / 2
+ else
+ if closing then
+ t_start = t_transition - fade_duration
+ t_end = t_transition
+ else
+ t_start = t_transition
+ t_end = t_transition + fade_duration
+ end
+ end
+ return {t_start = t_start, t_end = t_end}
+end
+
+function GraphAnimationCoordination:animateNodeAppearing()
+ local supergraph = self.supergraph
+ for i,vertex in ipairs(self.ugraph.vertices) do
+ local snapshot = supergraph:getSnapshot(vertex)
+ local interval = snapshot.interval
+ local supernode = supergraph:getSupervertex(vertex)
+ local representative = self.visible_objects[supernode][snapshot]
+ local overlapping_in = true -- init true for crossfading
+ local overlapping_out= true
+ local minimum_rest_time = math.max(0,vertex.options["minimum rest time"])
+ local allow_overlapping = vertex.options["overlapping transition"]
+ local fadein_duration = 0.01
+ local fadeout_duration = 0.01
+
+ if self.is_first[vertex] then
+ fadein_duration = self.ugraph.options["fadein time"]
+ overlapping_in = false or allow_overlapping
+ end
+ if self.is_last[vertex] then
+ fadeout_duration = self.ugraph.options["fadeout time"]
+ overlapping_out = false or allow_overlapping
+ end
+
+ if fadein_duration == math.huge or fadein_duration<0 then
+ fadein_duration = (interval.to-interval.from-minimum_rest_time)/2
+ if overlapping then fadein_duration = fadein_duration * 2 end
+ end
+ if fadeout_duration == math.huge or fadeout_duration<0 then
+ fadeout_duration = (interval.to-interval.from-minimum_rest_time)/2
+ if overlapping then fadeout_duration = fadeout_duration*2 end
+ end
+
+ local fin = compute_fade_times(interval.from, fadein_duration, overlapping_in, false)
+ local fout = compute_fade_times(interval.to, fadeout_duration, overlapping_out, true)
+
+ vertex.animations = vertex.animations or {}
+
+ if representative~= vertex then
+ table.insert(vertex.animations,{
+ attribute = "stage",
+ entries = { { t = 0, value = "false"}, },
+ options = {}
+ })
+ end
+
+ if interval.from > -math.huge and (vertex == representative or self.is_first[vertex]) then
+ -- only appears if the snapshot node is its own repr. or if in the prev snapshot is
+ -- no representative.
+ append_fade_animation(representative, 0, 1, fin.t_start, fin.t_end)
+ end
+ if interval.to < math.huge and (self.is_last[vertex] or self.last_rep[vertex]) then
+ -- The snapshot node only disappears when the node is not visible
+ -- in the next or (this=)last snapshot:
+ append_fade_animation(representative, 1, 0, fout.t_start, fout.t_end)
+ end
+ end
+end
+
+
+
+function GraphAnimationCoordination:animateEdgeAppearing()
+ local supergraph = self.supergraph
+ local graph = self.digraph
+ for _,edge in ipairs(graph.arcs) do
+ local snapshot = supergraph:getSnapshot(edge.head)
+ local int = snapshot.interval
+ local superarc = supergraph:getSuperarc(edge)
+ local representative = self.visible_objects[superarc][snapshot] or edge
+
+ local minimum_rest_time = math.max(0,edge.head.options["minimum rest time"]/2,
+ edge.tail.options["minimum rest time"]/2)
+
+ local appears = math.max(int.from, int.from)
+ local disappears = math.min(int.to, int.to)
+
+ local overlapping_in = true -- init true for crossfading
+ local overlapping_out= true
+ local fadein_duration = 0.01
+ local fadeout_duration = 0.01
+ local allow_overlapping = (edge.tail.options["overlapping transition"] and edge.head.options["overlapping transition"])
+
+ if self.is_first[edge] and not self.move_on_enter[edge] and not self.move_on_enter[edge.head] then
+ fadein_duration = self.ugraph.options["fadein time"]
+ overlapping_in = false or allow_overlapping
+ end
+
+ if self.is_last[edge] and not self.move_on_leave[edge] then
+ fadeout_duration = self.ugraph.options["fadeout time"]
+ overlapping_out = false or allow_overlapping
+ end
+
+
+ if self.is_first[edge]
+ and (self.move_on_enter[edge.head]
+ or self.move_on_enter[edge.tail] )
+ then
+ appears = snapshot.timestamp - minimum_rest_time
+ end
+ if self.is_last[edge] and
+ (self.move_on_leave[edge.head]
+ or self.move_on_leave[edge.tail]
+ ) then
+ disappears = snapshot.timestamp + minimum_rest_time
+ end
+
+ local fin = compute_fade_times(appears, fadein_duration, overlapping_in,false)
+ local fout = compute_fade_times(disappears,fadeout_duration,overlapping_out,true)
+
+ edge.animations = edge.animations or {}
+
+ if representative~=edge then
+ table.insert(edge.animations,{
+ attribute = "stage",
+ entries = { { t = 0, value = "false"}, },
+ options = {}})
+ end
+
+ -- Fade in:
+ if appears > -math.huge and (edge == representative or self.is_first[edge]) then
+ append_fade_animation(representative, 0, 1, fin.t_start, fin.t_end )
+ end
+
+ -- Fade out:
+ if disappears < math.huge and (self.is_last[edge] or self.last_rep[edge])then
+ append_fade_animation(representative, 1, 0, fout.t_start,fout.t_end )
+ end
+ end
+end
+
+function GraphAnimationCoordination:precomputeNodes()
+ local supergraph = self.supergraph
+
+ for _, supernode in ipairs(supergraph.vertices) do
+
+ local vis_nodes = {}
+ self.visible_objects[supernode] = vis_nodes
+
+ local any_previous_node = nil
+ local previous_representant = nil
+ local node_before = nil
+
+ for i, s in ipairs(supergraph.snapshots) do
+ local node = supergraph:getSnapshotVertex(supernode, s)
+
+ if node then
+ -- assume the node is the last node
+ self.is_last[node] = true
+
+ if node.options["modified"] then
+ -- modified
+ vis_nodes[s] = node
+ previous_representant = node
+ if any_previous_node then
+ self.last_rep[any_previous_node] = true
+ end
+ else
+ -- unmodified
+ previous_representant = previous_representant or node
+ vis_nodes[s] = previous_representant
+ end
+ any_previous_node = node
+
+ if node_before then
+ self.is_last[node_before] = false
+ self.previous_node[node] = node_before
+ self.next_node[node_before] = node
+
+ local do_move = (( node.pos.x ~= node_before.pos.x )
+ or (node.pos.y ~= node_before.pos.y))
+ self.move_on_enter[node] = do_move
+ self.move_on_leave[node_before] = do_move
+ else
+ self.is_first[node] = true
+ end
+ node_before = node
+ else
+ node_before = nil
+ end
+ end
+ end
+end
+
+function GraphAnimationCoordination:precomputeEdges()
+ -- 1. classify arcs (appearing, disappearing)
+ for _, arc in ipairs(self.digraph.arcs) do
+ local head = arc.head
+ local tail = arc.tail
+ if not ( self.is_first[head] or self.is_first[tail]) then
+ if not self.digraph:arc(self.previous_node[tail], self.previous_node[head]) then
+ -- new arc connects existing nodes
+ self.is_first[arc] = true
+ end
+ else
+ -- arc and at least one node is new.
+ self.is_first[arc] = true
+ end
+ if not ( self.is_last[head] or self.is_last[tail]) then
+ if not self.digraph:arc(self.next_node[tail],self.next_node[head]) then
+ -- arc disappears while nodes are still in the next snapshot
+ self.is_last[arc] = true
+ end
+ else
+ -- arc and at least one node disappears in the next snapshot
+ self.is_last[arc] = true
+ end
+ self.move_on_enter[arc] = self.move_on_enter[head] or self.move_on_enter[tail]
+ self.move_on_leave[arc] = self.move_on_leave[head] or self.move_on_leave[tail]
+ end
+
+ -- 2. precompute the unmodified edges
+ local supergraph = self.supergraph
+
+ for _, superarc in ipairs(supergraph.arcs) do
+ local vis_objects = {}
+ self.visible_objects[superarc] = vis_objects
+
+ local previous_arc
+ local previous_representant
+
+ for _, s in ipairs(supergraph.arc_snapshots[superarc]) do
+ local head = supergraph:getSnapshotVertex(superarc.head, s)
+ local tail = supergraph:getSnapshotVertex(superarc.tail, s)
+ -- use the digraph because the snapshot arc is not synced
+ local arc = self.digraph:arc(tail, head)
+
+ local modified = false
+ local opt_array = arc:optionsArray('modified')
+ for i = 1,#opt_array.aligned do
+ modified = modified or opt_array[i]
+ end
+
+ if modified or
+ not eq_arc(arc, previous_arc) or self.is_first[arc] then
+ --modified
+ previous_representant = arc
+ vis_objects[s] = arc
+ if previous_arc then
+ self.last_rep[previous_arc] = true
+ end
+ else
+ -- unmodified
+ previous_representant = previous_representant or arc
+ vis_objects[s] = previous_representant
+ end
+ previous_arc = arc
+ end
+ end
+end
+-- Done
+
+return GraphAnimationCoordination
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/experimental/evolving/GreedyTemporalCycleRemoval.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/experimental/evolving/GreedyTemporalCycleRemoval.lua
new file mode 100644
index 0000000000..0c348bcc8f
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/experimental/evolving/GreedyTemporalCycleRemoval.lua
@@ -0,0 +1,177 @@
+-- Copyright 2015 by Malte Skambath
+--
+-- 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
+
+
+local GreedyTemporalCycleRemoval = {}
+
+-- Imports
+local lib = require "pgf.gd.lib"
+local declare = require("pgf.gd.interface.InterfaceToAlgorithms").declare
+
+local Vertex = require "pgf.gd.model.Vertex"
+local Digraph = require "pgf.gd.model.Digraph"
+local Coordinate = require "pgf.gd.model.Coordinate"
+
+local PriorityQueue = require "pgf.gd.lib.PriorityQueue"
+
+-- Keys
+
+---
+
+declare {
+ key = "split critical arc head",
+ type = "boolean",
+ initial = true,
+ summary = "Specifies, that for a critical the tail node is separated"
+}
+
+---
+
+declare {
+ key = "split critical arc tail",
+ type = "boolean",
+ initial = true,
+ summary = "Specifies, that for a critical the tail node is separated"
+}
+
+---
+
+declare {
+ key = "greedy temporal cycle removal",
+ algorithm = GreedyTemporalCycleRemoval,
+ phase = "temporal cycle removal",
+ phase_default = true,
+ summary = [["
+ A temporal dependency cycle is a cyclic path in the supergraph of
+ an evolving graph. Use this key if you want remove all temporal
+ dependency cycles by a greedy strategy which incrementally inserts
+ edge checks if this edge creates a cycle and splits at least one node
+ into two supernode at a given time.
+ "]],
+ documentation = [["
+ See ToDo
+ "]]
+}
+
+-- Help functions
+local function reachable(graph, v, w)
+ local visited = {}
+ local queue = PriorityQueue.new()
+ queue:enqueue(v,1)
+ while not queue:isEmpty() do
+ local vertex = queue:dequeue()
+ if vertex==w then
+ return true
+ end
+ local outgoings = graph:outgoing(vertex)
+ for _, e in ipairs(outgoings) do
+ local head = e.head
+ if not visited[head] then
+ visited[head] = true
+ if head == w then
+ return true
+ else
+ queue:enqueue(head,1)
+ end
+ end
+ end
+ end
+ return false
+end
+
+-- Implementation
+
+function GreedyTemporalCycleRemoval:run()
+ local supergraph = assert(self.supergraph, "no supergraph passed")
+ local digraph = assert(self.digraph, "no digraph passed to the phase")
+ local split_tail = digraph.options["split critical arc tail"]
+ local split_head = digraph.options["split critical arc head"]
+ assert(split_tail or split_head, "without splitting nodes dependency cycles cannot be removed.")
+
+ self:iterativeCycleRemoval(supergraph, split_tail, split_head)
+end
+
+--
+-- Resolves all dependencies by splitting supernodes into multiple supernodes.
+-- To resolve a cycle each edge will be inserted into a dependency graph
+-- successively. Each time such edge closes a cycle the head and tail will
+-- be split at the related snapshot.
+--
+-- @param supergraph
+--
+function GreedyTemporalCycleRemoval:iterativeCycleRemoval(supergraph, split_tail, split_head)
+ -- Build up the global dependency graph
+ -- A supernode v directly depends on another supernode w if
+ -- there is a snapshot in which w is a child of w
+ local dependency_graph = Digraph.new(supergraph)
+ local stable_arcs = {}
+ for i, snapshot in ipairs(supergraph.snapshots) do
+ --local tree = snapshot.spanning_tree
+ for _,tree in ipairs(snapshot.spanning_trees) do
+ local new_arcs = {}
+
+ for _, e in ipairs(tree.arcs) do
+ if e.head.kind ~= "dummy" and e.tail.kind~="dummy" then
+ table.insert(new_arcs, e)
+
+ local sv = supergraph:getSupervertex(e.tail)
+ local sw = supergraph:getSupervertex(e.head)
+ local dep_arc = dependency_graph:arc(sv, sw)
+
+
+ if (not dep_arc) then
+ -- check if the edge v->w closes a cycle in the dependency graph
+ --pgf.debug{dependency_graph}
+ local cycle_arc = reachable(dependency_graph, sw, sv)
+ dep_arc = dependency_graph:connect(sv,sw)
+-- texio.write("\ncheck ".. sv.name.."->" .. sw.name)
+ if cycle_arc then
+ if split_tail then
+ supergraph:splitSupervertex(sv, { [1]=snapshot })
+ end
+ if split_head then
+ supergraph:splitSupervertex(sw, { [1]=snapshot })
+ end
+
+ -- rebuild dependency graph
+ dependency_graph = Digraph.new(supergraph)
+
+ for _, arc in ipairs(stable_arcs) do
+ dependency_graph:connect(arc.tail, arc.head)
+ end
+
+ for _, arc in ipairs(new_arcs) do
+ local sv = supergraph:getSupervertex(arc.tail)
+ local sw = supergraph:getSupervertex(arc.head)
+ dependency_graph:connect(sv, sw)
+ end
+ end -- end of resolve cycle_arc
+ end
+ end
+ end
+ -- Stable Arcs:
+ for _, arc in ipairs(new_arcs) do
+
+ local sv = supergraph:getSupervertex(arc.tail)
+ local sw = supergraph:getSupervertex(arc.head)
+ local deparc = dependency_graph:arc(sv, sw)
+-- if not deparc or not stable_arcs[deparc] then
+-- stable_arcs[deparc] = true
+ table.insert(stable_arcs, deparc)
+-- end
+
+ end
+ end -- end for spanning_tree
+ end -- end for snapshot
+end
+
+
+-- Done
+
+return GreedyTemporalCycleRemoval
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/experimental/evolving/Skambath2016.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/experimental/evolving/Skambath2016.lua
new file mode 100644
index 0000000000..2beec1e216
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/experimental/evolving/Skambath2016.lua
@@ -0,0 +1,875 @@
+-- Copyright 2015 by Malte Skambath
+--
+-- 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
+--
+
+-- Imports
+require "pgf.gd.trees.ChildSpec"
+
+local Digraph = require "pgf.gd.model.Digraph"
+local Vertex = require "pgf.gd.model.Vertex"
+
+local declare = require("pgf.gd.interface.InterfaceToAlgorithms").declare
+local layered = require "pgf.gd.layered"
+local tlayered = require "pgf.gd.experimental.evolving.layered"
+local SpanningTreeComputation = require "pgf.gd.trees.SpanningTreeComputation"
+local lib = require "pgf.gd.lib"
+
+local Storage = require "pgf.gd.lib.Storage"
+local PriorityQueue = require "pgf.gd.lib.PriorityQueue"
+
+local Supergraph = require "pgf.gd.experimental.evolving.Supergraph"
+
+local LayoutPipeline = require "pgf.gd.control.LayoutPipeline"
+local Direct = require "pgf.gd.lib.Direct"
+
+--
+--
+local Skambath2016 = {}
+
+
+---
+declare {
+ key = "animated tree layout",
+ algorithm = Skambath2016,
+ postconditions = {
+ upward_oriented = true
+ },
+ documentation_in = "pgf.gd.evolving.doc"
+}
+
+---
+declare {
+ key = "animated binary tree layout",
+ use = {
+ { key = "animated tree layout" },
+ { key = "minimum number of children", value = 2 },
+ { key = "significant sep", value = 0 },
+ },
+
+ documentation_in = "pgf.gd.evolving.doc"
+}
+
+---
+declare {
+ key = "extended animated binary tree layout",
+ use = {
+ { key = "animated tree layout" },
+ { key = "minimum number of children", value=2 },
+ { key = "missing nodes get space" },
+ { key = "significant sep", value = 0 }
+ },
+ documentation_in = "pgf.gd.evolving.doc"
+}
+
+
+
+
+
+-- Help functions
+
+---
+-- Borders models the borderlines / the line of border vertices
+-- of a tree or subtree structure which can change over time.
+-- Each ordered and rooted tree or subtree has vertices per layer for which they
+-- are on the outer places. On the left or respectively on the right.
+-- The field |left| and |right| stores the both borderlines.
+-- A \emph{borderline} is an array. This array contains for each layer of the tree
+-- a table mapping a given snapshot to the related border-vertex of the layer
+-- in the snapshot.
+--
+-- @field left
+-- @field right
+--
+local Borders = {}
+Borders.__index = Borders
+
+
+-- Namespace
+
+--require("pgf.gd.experimental.evolving").Borders = Borders
+
+
+--- Create a new tree border description.
+--
+--
+function Borders.new()
+ return setmetatable( {left = {}, right = {}}, Borders )
+end
+
+function Borders:addBoth(layer, snapshot, vertex)
+ local lleft = self.left[layer] or {}
+ local lright = self.right[layer] or {}
+ assert(not lleft[snapshot] and not lright[snapshot], "borders already defined for given layer and snapshot")
+ lleft[snapshot] = vertex
+ lright[snapshot] = vertex
+ self.left[layer] = lleft
+ self.right[layer] = lright
+end
+
+function Borders:appendBelow(level, borders)
+ assert(borders, "invalid argument for borders. Value must not be 'nil'.")
+ assert((#self.left == #self.right) and (#self.left == level), "cannot be appended")
+ local new_level = borders:getLevel()
+ for i = 1, new_level do
+ self.left[i + level] = borders.left[i]
+ self.right[i + level] = borders.right[i]
+ end
+end
+
+---
+--
+-- @return the number of levels in which bordervertices exists.
+function Borders:getLevel()
+ assert(#self.left == #self.right, "different levels")
+ return #self.left
+end
+
+function Borders.copyBordersAtSnapshot(source, target, snapshot)
+ local source_level = source:getLevel()
+ for i = 1, source_level do
+ level_border_left = target.left[i] or {}
+ level_border_right = target.right[i] or {}
+ assert(not level_border_left[snapshot] and not level_border_right[snapshot],
+ "border for a given snapshot already defined")
+ level_border_left[snapshot] = source.left[i][snapshot]
+ level_border_right[snapshot] = source.right[i][snapshot]
+ target.left[i] = level_border_left
+ target.right[i] = level_border_right
+ end
+end
+
+--
+-- Adapt existing borders with the borders of a group which will be
+-- placed on the right next to the existing borders. For each level
+-- and time in which the group has a border the borders on the right
+-- will be replaced with this border. If it is not existing in the old
+-- borders then also the left border will be adapted
+--
+-- @param borders The existing borders. These will be modified with
+-- the borders of
+--
+-- @param group_borders The borders of the group
+--
+-- @param snapshots All snapshots in which checking for distances is necessary.
+--
+local function adapt_borders(borders, group_borders, snapshots, ignore_dummies)
+ for level = 1, group_borders:getLevel() do
+ local l = borders.left[level] or {}
+ local r = borders.right[level] or {}
+ for _, s in pairs(snapshots) do
+ if ignore_dummies then
+ local gls,grs = group_borders.left[level][s], group_borders.right[level][s]
+ if gls~=nil then
+ if gls.kind~="dummy" then
+ l[s] = l[s] or gls
+ end
+ end
+ if grs~=nil then
+ if grs.kind~="dummy" then
+ r[s] = grs or r[s]
+ end
+ end
+ else
+ l[s] = l[s] or group_borders.left[level][s]
+ r[s] = group_borders.right[level][s] or r[s]
+ end
+
+ end
+ borders.left[level] = l
+ borders.right[level] = r
+ end
+
+end
+
+--
+-- Shift all vertices of a group and their descendants
+-- for a certain length into horizontal direction.
+--
+-- @param shift the length all involved vertices
+-- should be shifted in horizontal direction
+--
+--
+-- @param group A group of the spanning trees that should be shifted.
+-- A group entry has to map snapshots to root nodes
+--
+-- @param snapshots An array of at least all snapshots in which the group
+-- has a vertex
+--
+-- @param descendants A table or Storage storing the list of descendants
+-- for each vertex
+--
+-- @return the highest x-coordinate of vertices in the group after the shift.
+-- If there is no vertex which was shifted then -math.huge
+-- will be returned
+--
+local function shift_group(shift, group, snapshots, descendants)
+ assert(group,"no group passed")
+ assert(shift~=math.huge and shift ~=-math.huge,
+ "shift must be a valid finite length")
+ local shifted = {} -- remember which vertex was shifted during this run
+ local anchor = -math.huge
+ for _, s in ipairs(snapshots) do
+ local v = group[s]
+ if not shifted[v] and v then
+ v.pos.x = v.pos.x + shift
+ shifted[v] = true
+
+ -- also shift all descendants of the group vertex
+ for _, d in ipairs(descendants[v]) do
+ if not shifted[d] then
+ d.pos.x = d.pos.x + shift
+ shifted[d] = true
+ end
+ end
+ anchor = math.max(anchor, v.pos.x )
+ end
+ end
+ return anchor
+end
+
+--
+-- Traverse through the spanning tree |tree| of a snapshot and sort
+-- the child nodes into groups. A group summarizes for a given parent
+-- node all children node over time that are at the same child
+-- position. The k-th child group groups[i] maps each snapshot to the
+-- k-th node in the related snapshot.
+--
+--
+-- @field supergraph the supergraph
+--
+-- @field tree the tree or spanning tree to decide the indices of the
+-- child nodes
+--
+-- @field childgroups a Storage which contains the list of childgroups
+-- for each supernode
+--
+-- @field snapshot
+--
+local function precompute_childgroups(supergraph, tree, node, childgroups, snapshot)
+ local outgoings = tree:outgoing(node)
+ if #outgoings > 0 then
+ local supervertex = supergraph:getSupervertex(node)
+ local groups = childgroups[supervertex] or {}
+ for i, e in ipairs(outgoings) do
+
+ group = groups[i] or {}
+ assert(e.head, "no edge")
+ group[snapshot] = e.head
+ groups[i] = group
+ precompute_childgroups(supergraph, tree, e.head, childgroups, snapshot)
+ end
+ assert(supervertex, "no mv")
+ childgroups[supervertex] = groups
+ end
+end
+
+--
+-- Use this function to compute the horizontal positions of all
+-- vertices in a tree by accumulation of the relative shifts on the
+-- path from the root to the vertex recursively.
+--
+-- @param tree the tree in which the vertex's position should be
+-- computed.
+--
+-- @param vertex the next vertex that gets its absolute coordinate.
+--
+-- @param shifts a Storage, which stores for each node the relative
+-- shift between the vertex and its parent.
+--
+-- @param abs_shift the sum of all relative shifts on the path from
+-- the root to the vertex.
+--
+local function accumulate_hpos(tree, vertex, shifts, abs_shift)
+ local new_shift = abs_shift + shifts[vertex]
+ local test = vertex.pos.x
+ vertex.pos.x = new_shift
+-- if vertex.pos.x - test > 0.0001 then texio.write("X")end
+ local outgoings = tree:outgoing(vertex)
+ for _, e in ipairs(outgoings) do
+ accumulate_hpos(tree, e.head, shifts, new_shift)
+ end
+end
+
+
+local function get_next(border_pair, next)
+ local nl = next.left[border_pair.left]
+ local nr = next.right[border_pair.right]
+ assert ((nl and nr) or (not nl and not nr))
+ return {left = nl, right = nr,
+ }
+end
+
+local function add_shift(abs_shift, border_pair, next)
+ abs_shift.left = abs_shift.left + next.left_shift[border_pair.left]
+ abs_shift.right = abs_shift.right + next.right_shift[border_pair.right]
+end
+
+--
+-- Given a tree, computes the required distance between the i-th and the (i+1)-th subtree
+-- of the vertex |snapshot_vertex|.
+--
+-- @param shifts a Storage, which contains for each vertex the relative horizontal shift
+-- to its parent vertex.
+--
+function Skambath2016:computeRequiredDistance(tree, vertex, i, shifts, next)
+ local outgoings = tree:outgoing(vertex)
+-- texio.write("\n::"..vertex.name.. " "..i.."|"..(i+1))
+ if #outgoings > 0 then
+ local clumb = {left = outgoings[1].head,right = outgoings[i].head}
+ if clumb.right.kind=="dummy" then shifts[clumb.right] = 0 end
+ local v0 = outgoings[i].head
+ local v1 = outgoings[i+1].head
+ local shift = layered.ideal_sibling_distance(self.adjusted_bb, self.ugraph, v0, v1) + shifts[clumb.right]
+ local last0 = {left = clumb.left, right = clumb.right}
+ local last1 = {left = v1, right = v1}
+ local next0 = get_next(last0, next)
+ local next1 = get_next(last1, next)
+ local abs_shift0 = {left = shifts[clumb.left], right = shifts[clumb.right]}
+ local abs_shift1 = {left = 0, right = 0}
+
+ while (next0.left and next1.left) do
+ add_shift(abs_shift0, last0, next)
+ add_shift(abs_shift1, last1, next)
+
+ shift = math.max(shift,
+ layered.ideal_sibling_distance(self.adjusted_bb,
+ self.ugraph,
+ next0.right,
+ next1.left)
+ + abs_shift0.right - abs_shift1.left)
+-- texio.write("\n | "..(next0.right.name or "dummy").."<->"..(next1.left.name or "dummy").." :\t"..shift)
+ last0, last1 = next0, next1
+ next0 = get_next(next0, next)
+ next1 = get_next(next1, next)
+ end
+ return shift, {l0 = last0, l1 = last1, n0 = next0, n1 = next1,abs_shift1 = abs_shift1,abs_shift0=abs_shift0}
+ -- end
+ else
+ return 0
+ end
+end
+
+local function apply_shift(tree, vertex, i, shifts, next, border_ptr, shift)
+ local outgoings = tree:outgoing(vertex)
+-- texio.write("\n" .. (vertex.name or "dummy")..": ".. shift )
+ if #outgoings >= (i+1) then
+ assert(border_ptr, "unexpected error")
+ local last0 = border_ptr.l0
+ local last1 = border_ptr.l1
+ local next0 = border_ptr.n0
+ local next1 = border_ptr.n1
+ local abs0 = border_ptr.abs_shift0
+ local abs1 = border_ptr.abs_shift1
+ local vbase = outgoings[1].head -- before centering the 1st vertex is at x=0
+ local v0 = outgoings[i].head
+ local v1 = outgoings[i+1].head
+ if v0.kind=="dummy" then shifts[v0] = 0 end
+ shifts[v1] = shifts[vbase] + shift
+ if next0.left then
+ assert(next0.right and next0.left, "failA")
+ -- pointer from T_i to T_{i+0}
+ next.right[last1.right] = next0.right
+ next.right_shift[last1.right] = - shift - abs1.right + (abs0.right + next.right_shift[last0.right])
+ elseif next1.right then
+ assert(next1.right and next1.left, "")
+ -- pointer from T_{i+0} to T_i
+ -- texio.write(last0.left .." -> " ..next1.left)
+ next.left[last0.left] = next1.left
+-- pgf.debug{last0,abs0,abs1,last1}
+ next.left_shift[last0.left] = shift - abs0.left + (abs1.left + next.left_shift[last1.left] )
+
+ else
+ -- both trees have the same height
+ end
+ end
+end
+
+-- Implementation
+
+function Skambath2016:run()
+ local layers = Storage.new()
+ local descendants = Storage.new()
+ local childgroups = Storage.new()
+
+ local phases = self.digraph.options.algorithm_phases
+
+ local so_class = phases['supergraph optimization']
+ local ga_class = phases['evolving graph animation']
+ local cr_class = phases['temporal cycle removal']
+
+ self.extended_version = self.digraph.options['missing nodes get space']
+ self.supergraph = Supergraph.generateSupergraph(self.digraph)
+ local supergraph_original = Supergraph.generateSupergraph(self.digraph)
+
+ -- optimize graph by splitting nodes by trivial criteria
+ so_class.new {
+ main_algorithm = self,
+ supergraph = self.supergraph,
+ digraph = self.digraph,
+ ugraph = self.ugraph
+ }:run()
+
+
+ self:precomputeSpanningTrees()
+
+ -- Resolve cyclic dependencies if exists.
+ cr_class.new {
+ main_algorithm = self,
+ supergraph = self.supergraph,
+ digraph = self.digraph,
+ }:run()
+
+
+
+ self:precomputeDescendants(layers, descendants)
+ self:precomputeChildgroups(childgroups)
+
+ self:computeHorizontalLayout(childgroups, descendants)
+-- self:computeHorizontalLayoutFast()
+
+ -- vertical positions
+ tlayered.arrange_layers_by_baselines(layers,
+ self.adjusted_bb,
+ self.ugraph,
+ self.supergraph.snapshots,
+ self.supergraph.vertex_snapshots)
+
+ -- animate graph
+ ga_class.new {
+ main_algorithm = self,
+ supergraph = supergraph_original,
+ digraph = self.digraph,
+ ugraph = self.ugraph
+ }:run()
+end
+
+--
+-- Compute the required shift value for a second tree to guarantee
+-- a required node distance.
+-- @field right_borders The Border data structure for the right border of
+-- the left tree
+-- @field left_borders The Border data structure for the left border of
+-- the right tree
+-- @field selected_snapshots if you set this value with an array of snapshots
+-- only the predefined snapshots are used in the border
+-- computation.
+--
+function Skambath2016:computeMinShift(right_borders, left_borders, selected_snapshots)
+ local shift = -math.huge
+ local max_level = math.min(#right_borders, #left_borders)
+ local first_shift = 0
+ local snapshots = selected_snapshots or self.supergraph.snapshots
+
+ for layer = 1, max_level do
+ local rb, lb = right_borders[layer], left_borders[layer]
+ for _,s in ipairs(snapshots) do
+
+ local v1,v2 = rb[s],lb[s]
+ if v1 and v2 then
+ local local_shift = layered.ideal_sibling_distance(self.adjusted_bb, self.ugraph, v1, v2) + v1.pos.x - v2.pos.x
+ shift = math.max(shift, local_shift)
+ end
+ end
+ if layer == 1 then
+ first_shift = shift
+ end
+ end
+
+ local is_significant = false
+
+ if max_level > 1 and shift<=first_shift then
+ -- if the necessary shift of the subtrees
+ -- is the minimum required shift between two
+ -- nodes than a node is significant
+ is_significant = true
+ end
+
+ if shift <= -math.huge then
+ shift = 0
+ end
+
+ if is_significant then
+ shift = shift + self.ugraph.options['significant sep']
+ end
+
+ return shift
+end
+
+
+--
+-- The main algorithm: This method computes the layout for each vertex.
+-- For this all supervertices are visited in a topological order to their dependency.
+-- If a . This requires the supergraph to be acyclic. If this is not the case
+-- the calling process has to remove all cycles otherwise the x-coordinate will
+-- not be computed for every vertex.
+--
+function Skambath2016:computeHorizontalLayout(groups, descendants)
+ local subtree_borders = Storage.new()
+ local dep_counter = {}
+ local visited = {}
+ local queue = PriorityQueue.new()
+ local dependency_graph = Digraph.new()
+ for _, vertex in ipairs(self.supergraph.vertices) do
+ dep_counter[vertex] = 0
+ dependency_graph:add {vertex}
+ end
+
+ -- 1. Initialize Dependencies
+
+ for _, snapshot in ipairs(self.supergraph.snapshots) do
+ for _, spanning_tree in ipairs(snapshot.spanning_trees) do
+ for _, arc in ipairs(spanning_tree.arcs) do
+
+ local head = self.supergraph:getSupervertex(arc.head)
+ local tail = self.supergraph:getSupervertex(arc.tail)
+
+ if(head and tail) then
+ if not dependency_graph:arc(tail, head) then
+ dependency_graph:connect(tail, head)
+ dep_counter[tail] = dep_counter[tail] + 1
+ end
+ end
+ end
+ end
+ end
+
+ -- 2. Find independent vertices
+ for _, vertex in ipairs(dependency_graph.vertices) do
+ local outgoings = dependency_graph:outgoing(vertex)
+ if #outgoings == 0 then
+ queue:enqueue(vertex, 1)
+ end
+ end
+
+ -- 2.
+ while not queue:isEmpty() do
+ local vertex = queue:dequeue()
+ local vertex_snapshots = self.supergraph:getSnapshots(vertex)
+
+ -- a. Resolve dependencies on this vertex:
+ local incomings = dependency_graph:incoming(vertex)
+ for _, e in ipairs(incomings) do
+ dep_counter[e.tail] = dep_counter[e.tail] - 1
+ if dep_counter[e.tail] == 0 then
+ queue:enqueue(e.tail, 1)
+ end
+ end
+
+ -- b. Compute borders of this supervertex:
+ local vertex_borders = Borders.new()
+ for _, s in ipairs(vertex_snapshots) do
+ local snapshot_vertex = self.supergraph:getSnapshotVertex(vertex, s)
+ vertex_borders:addBoth(1, s, snapshot_vertex)
+ snapshot_vertex.pos.x = 0
+ snapshot_vertex.pos.y = 0
+ end
+
+ local vertex_groups = groups[vertex]
+ local last_pos_x = 0
+ if vertex_groups then
+ -- c. Compute borders of groups:
+ local all_group_borders = {}
+ for i, group in ipairs(vertex_groups) do
+ local group_boders = Borders.new()
+ for _,s in ipairs(vertex_snapshots) do
+ local child = group[s]
+ if child then
+ local child_borders
+ if not (child.kind == "dummy") then
+ local superchild = self.supergraph:getSupervertex(child)
+ child_borders = subtree_borders[superchild] or Borders.new()
+ else
+ child_borders = Borders.new()
+ child_borders:addBoth(1, s, child)
+ end
+ assert(child.pos~=math.huge, "invalid child pos")
+ shift_group(-child.pos.x,{[s]=child},{[1]=s}, descendants)
+ Borders.copyBordersAtSnapshot(child_borders, group_boders, s)
+ end
+ end
+ all_group_borders[i] = group_boders
+ end
+
+ -- d. Place groups and merge borders of groups:
+ local last_group = nil
+ last_pos_x = 0
+ local merged_borders = Borders.new()
+ local final_borders = Borders.new()
+ for i, group in ipairs(vertex_groups) do
+ local group_borders = all_group_borders[i]
+ if last_group_borders then
+ -- i. compute minimal shift
+
+ local shift
+ shift = self:computeMinShift(merged_borders.right, group_borders.left)
+
+
+ assert(shift ~= math.huge and shift~=-math.huge, "invalid shift")
+
+ -- ii. shift group
+ local anchor = shift_group(shift, group,vertex_snapshots, descendants)
+ last_pos_x = anchor
+ end
+ last_group_borders = group_borders
+
+ -- iii. adapt borders
+ adapt_borders(merged_borders,
+ group_borders,
+ self.supergraph.snapshots)
+ adapt_borders(final_borders,
+ group_borders,
+ self.supergraph.snapshots,
+ not self.extended_version)
+ end -- for (group)
+ vertex_borders:appendBelow(1, final_borders)
+ end
+ -- e. store borders:
+ assert(last_pos_x~=math.huge and last_pos_x~=-math.huge, "invalid position")
+ local x = ((last_pos_x) - 0)/2 + 0
+ assert(x~=math.huge and x~=-math.huge, "invalid position")
+ for _,s in ipairs(vertex_snapshots) do
+ local snapshot_vertex = self.supergraph:getSnapshotVertex(vertex, s)
+ snapshot_vertex.pos.x = x
+ end
+
+ subtree_borders[vertex] = vertex_borders
+ end
+
+ -- align roots
+ for _, s in ipairs(self.supergraph.snapshots) do
+ local lastroot
+ local rborder
+ for i, spanning_tree in ipairs(s.spanning_trees) do
+ local root = spanning_tree.root
+ local rootborders = subtree_borders[self.supergraph:getSupervertex(root)]
+ shift_group(-root.pos.x,{[s]=root},{[1]=s}, descendants)
+ if i>1 then
+ local l = subtree_borders[self.supergraph:getSupervertex(lastroot)]
+ local r = rootborders
+ shift = math.max(self:computeMinShift(l.right, r.left, {[1]=s}),
+ self:computeMinShift(rborder.right,r.left, {[1]=s}))
+ shift_group(shift,{[s]=root},{[1]=s}, descendants)
+ else
+ rborder = Borders.new()
+ end
+ adapt_borders(rborder,rootborders,self.supergraph.snapshots,false)
+ lastroot = root
+ end
+ end
+end
+
+--
+-- The main algorithm: This method computes the layout for each vertex.
+-- For this all supervertices are visited in a topological order to their dependency.
+-- If a . This requires the supergraph to be acyclic. If this is not the case
+-- the calling process has to remove all cycles otherwise the x-coordinate will
+-- not be computed for every vertex.
+--
+function Skambath2016:computeHorizontalLayoutFast()
+ local all_trees = Storage.new()
+ local dep_counter = {}
+ local visited = {}
+ local queue = PriorityQueue.new()
+ local dependency_graph = Digraph.new()
+ local shifts = Storage.new()
+ local next = Storage.new()
+ for _, vertex in ipairs(self.supergraph.vertices) do
+ dep_counter[vertex] = 0
+ dependency_graph:add {vertex}
+ end
+
+
+ -- I. Initialize Dependencies (Build Dependency Graph)
+ for _, snapshot in ipairs(self.supergraph.snapshots) do
+ for _, spanning_tree in ipairs(snapshot.spanning_trees) do
+ table.insert(all_trees, spanning_tree)
+ shifts[spanning_tree] = Storage.new()
+ next[spanning_tree] = {left= Storage.new(),
+ right= Storage.new(),
+ left_shift = Storage.new(),
+ right_shift = Storage.new()
+ }
+
+ for _, arc in ipairs(spanning_tree.arcs) do
+ local head = self.supergraph:getSupervertex(arc.head)
+ local tail = self.supergraph:getSupervertex(arc.tail)
+
+ if(head and tail) then
+ if not dependency_graph:arc(tail, head) then
+ dependency_graph:connect(tail, head)
+ dep_counter[tail] = dep_counter[tail] + 1
+ end
+ end
+ end
+ end
+ end
+
+ -- II. Visit vertices in topological ordering
+ -- Find independent vertices
+ for _, vertex in ipairs(dependency_graph.vertices) do
+ local outgoings = dependency_graph:outgoing(vertex)
+ if #outgoings == 0 then
+ queue:enqueue(vertex, 1)
+ end
+ end
+
+ while not queue:isEmpty() do
+ -- Next node in topological order
+ local vertex = queue:dequeue()
+-- texio.write("\n\n --- "..vertex.name .. " ---")
+ --pgf.debug{next}
+ local vertex_snapshots = self.supergraph:getSnapshots(vertex)
+
+ -- a. Resolve dependencies on this vertex:
+ local incomings = dependency_graph:incoming(vertex)
+ for _, e in ipairs(incomings) do
+ dep_counter[e.tail] = dep_counter[e.tail] - 1
+ if dep_counter[e.tail] == 0 then
+ queue:enqueue(e.tail, 1)
+ end
+ end
+
+ -- b. Compute maximum number of children over time:
+ local num_children = 0
+ for _, s in ipairs(vertex_snapshots) do
+ local v = self.supergraph:getSnapshotVertex(vertex, s)
+ local tree = s.spanning_trees[1]
+ num_children = math.max(num_children, #(tree:outgoing(v)))
+ shifts[tree][v] = 0
+ end
+
+ -- c. Shift all subtrees in all snapshots:
+ local hlp_ptr = Storage.new()
+ local max_shift = 0
+ for i = 1, (num_children - 1) do
+ -- i) Compute the necessary shift between the i-th and (i+1)-th subtrees (per snapshot):
+ local min_shift = 0
+ for t, s in ipairs(vertex_snapshots) do
+ local snapshot_vertex = self.supergraph:getSnapshotVertex(vertex, s)
+ local tree = s.spanning_trees[1]
+ local req_shift, hptr
+ req_shift, hptr = self:computeRequiredDistance(tree,
+ snapshot_vertex,
+ i,
+ shifts[tree],
+ next[tree]
+ )
+ hlp_ptr[t] = hptr
+-- texio.write(" -> \t"..req_shift)
+ min_shift = math.max(min_shift, req_shift)
+ end
+
+-- texio.write("\n \t\t".. min_shift )
+
+ -- ii) Synchronize distance between neighbored subtrees and apply shifts
+ for t, s in ipairs(vertex_snapshots) do
+ local snapshot_vertex = self.supergraph:getSnapshotVertex(vertex, s)
+ local tree = s.spanning_trees[1]
+ apply_shift(tree, snapshot_vertex, i, shifts[tree], next[tree], hlp_ptr[t], min_shift)
+ end
+
+ max_shift = min_shift
+ end
+
+ for t, s in ipairs(vertex_snapshots) do
+ local snapshot_vertex = self.supergraph:getSnapshotVertex(vertex, s)
+ local tree = s.spanning_trees[1]
+ local outgoings = tree:outgoing(snapshot_vertex)
+
+-- next[tree].left[snapshot_vertex] = outgoings[1].head
+
+
+ for i = 1,#outgoings do
+ if i==1 then
+ next[tree].left_shift[snapshot_vertex] = - max_shift / 2
+ next[tree].left[snapshot_vertex]= outgoings[i].head
+ end
+ shifts[tree][outgoings[i].head] = shifts[tree][outgoings[i].head] - max_shift / 2
+ next[tree].right[snapshot_vertex] = outgoings[i].head
+ next[tree].right_shift[snapshot_vertex] = shifts[tree][outgoings[i].head]
+ end
+
+ end
+
+ end -- end while (all vertices have been processed)
+
+ -- III. Accumulate absolute horizontal coordinates
+ for _, tree in ipairs(all_trees) do
+ accumulate_hpos(tree, tree.root, shifts[tree], 0)
+ end
+end
+
+
+
+
+function Skambath2016:precomputeTreeDescendants(tree, node, depth, layers, descendants)
+ local my_descendants = { node }
+
+ for _,arc in ipairs(tree:outgoing(node)) do
+ local head = arc.head
+ self:precomputeTreeDescendants(tree, head, depth+1, layers, descendants)
+ for _,d in ipairs(descendants[head]) do
+ my_descendants[#my_descendants + 1] = d
+ end
+ end
+ layers[node] = depth
+ descendants[node] = my_descendants
+end
+
+function Skambath2016:precomputeDescendants(layers, descendants)
+ for _,snapshot in ipairs(self.supergraph.snapshots) do
+ for _, spanning_tree in ipairs(snapshot.spanning_trees) do
+ self:precomputeTreeDescendants(spanning_tree, spanning_tree.root, 1, layers, descendants)
+ end
+ end
+end
+
+
+--
+--
+--
+function Skambath2016:precomputeChildgroups(childgroups)
+ for _,s in ipairs(self.supergraph.snapshots) do
+ for _,spanning_tree in ipairs(s.spanning_trees) do
+ precompute_childgroups(self.supergraph, spanning_tree, spanning_tree.root, childgroups, s)
+ end
+ end
+end
+
+--
+-- Compute a for each connected component of each
+-- snapshot and appends the result for a snapshot s to
+-- the array s.spanning_trees.
+--
+function Skambath2016:precomputeSpanningTrees()
+ local events = assert(self.scope.events,
+ "no events found for the spanning tree computation")
+
+ for i, s in ipairs(self.supergraph.snapshots) do
+ -- The involved snapshot graph:
+ local s_copy = Digraph.new(s)
+ for _,a in ipairs(s.arcs) do
+ local new_a = s_copy:connect(a.tail,a.head)
+ new_a.syntactic_edges = a.syntactic_edges
+ end
+ s.spanning_trees = s.spanning_trees or {}
+ -- Step 1: Decompose the snapshot into its connected components
+ local syntactic_components = LayoutPipeline.decompose(s_copy)
+ for i, syntactic_component in ipairs (syntactic_components) do
+ local tree = SpanningTreeComputation.computeSpanningTree(syntactic_component, true, events)
+ s.spanning_trees[i] = tree
+ end
+ end
+end
+
+return Skambath2016
+
+
+
+
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/experimental/evolving/Supergraph.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/experimental/evolving/Supergraph.lua
new file mode 100644
index 0000000000..99e79919b5
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/experimental/evolving/Supergraph.lua
@@ -0,0 +1,571 @@
+-- Copyright 2015 by Malte Skambath
+--
+-- This file may be distributed an/or modified
+--
+-- 1. under the LaTeX Project Public License and/or
+-- 2. under the GNU Public License
+--
+
+-- Imports
+local lib = require "pgf.gd.lib"
+
+local Vertex = require "pgf.gd.model.Vertex"
+local Digraph = require "pgf.gd.model.Digraph"
+local Storage = require "pgf.gd.lib.Storage"
+
+
+local declare = require("pgf.gd.interface.InterfaceToAlgorithms").declare
+
+---
+-- Each |Supergraph| instance is a |Digraph| instance which represents
+-- the graph by union operation on all graphs G_i of an evolving graph
+-- $G=(G_1, G_2, \dots, G_n)$. Additional to that all references to
+-- the snapshot-graphs are shared such that is possible to get access
+-- to all vertices for each snapshot graph in a sequence. A vertex of
+-- an evolving graph may exists at different times, thus in in
+-- different snapshots. Each vertex will be a vertex in the supergraph
+-- and if there is a single snapshot in which two vertices are
+-- connected by an edge they are connected in the supergraph.
+--
+-- Note that in \tikzname\ a \emph{node} is more than a single dot. A node
+-- has a content and different properties like background-color or a
+-- shape. Formally this can be modeled by function mapping vertices
+-- to their properties. For evolving graphs this could be done in the
+-- same way. As this is difficult to be realized in PGF because there
+-- is no basic support for time dependent properties on nodes, each
+-- vertex will be displayed over time by different single
+-- (snapshot-)nodes which can have different visual properties. This
+-- means for a vertex which we call |supervertex| in the following we
+-- will have a (snapshot-)node for each time stamp.
+--
+-- \medskip
+-- \noindent\emph{Snapshots.}
+-- Since an evolving graph is a sequence of different snapshot-graphs
+-- $G_i$ each snapshot is assigned to a time
+--
+--
+-- @field vertex_snapshots This storage maps each pgf-node to the snapshots
+-- in which they are visible.
+--
+-- @field supervertices This storage maps each pgf-node to its supervertex
+-- which represents all pgf-vertices assigned to the same node
+--
+-- @field supervertices_by_id This storage maps a node identifier to the
+-- related supervertex such that PGF-nodes which belonging to
+-- the same superverticex can be identified
+--
+-- @field snapshots An array of all snapshots. Sorted in ascending order
+-- over the timestamps of the snapshots.
+--
+-- @field arc_snapshots A table storing all snapshots of a supervertex in which
+-- the related nodes are connected. Using a snapshot as key you can check
+-- if a given snapshot is in the array.
+--
+-- Assume we want to iterate over all snapshots
+-- for a certain pair of supernodes in which they are connected
+-- by an arc. The arc_snapshots storage helps in this case:
+-- %
+-- \begin{codeexample}[code only, tikz syntax=false]
+-- local supergraph = Supergraph.generateSupergraph(self.digraph)
+-- local u = supergraph.vertices[1]
+-- local v = supergraph.vertices[2]
+--
+-- local snapshots = supergraph.arc_snapshots[supergraph:arc(u, v)]
+-- for _, snapshot in ipairs(snapshots) do
+-- do_something(snapshot)
+-- end
+-- \end{codeexample}
+--
+local Supergraph = lib.class { base_class = Digraph }
+
+-- Namespace
+--require("pgf.gd.experimental.evolving").Supergraph = Supergraph
+
+Supergraph.__index =
+ function (t, k)
+ if k == "arcs" then
+ return Digraph.__index(t,k)
+ else
+ return rawget(Supergraph, k) or rawget(Digraph, k)
+ end
+ end
+
+function Supergraph.new(initial)
+ local supergraph = Digraph.new(initial)
+ setmetatable(supergraph, Supergraph)
+
+ supergraph.vertex_snapshots = Storage.new()
+ supergraph.supervertices = Storage.new()
+ supergraph.supervertices_by_id = {}
+ supergraph.arc_snapshots = Storage.newTableStorage()
+
+ return supergraph
+
+end
+
+
+local get_snapshot
+
+---
+-- Generate or extract a snapshot instance for a given snapshot time.
+--
+-- @param snapshots An array of all existing snapshots
+-- @param timestamps A table which maps known timestamps to their
+-- related snapshots
+-- @param ugraph The ugraph of the underlying graph structure
+-- @param snapshot_time
+--
+-- @return The snapshot instance found in the snapshots array for the
+-- wanted timestamp snapshot_time if it doesn't exists a new snapshot
+-- will be generated and added to the arrays
+--
+function get_snapshot(snapshots, timestamps, ugraph, snapshot_time)
+ local snapshot
+ local snapshot_idx = timestamps[snapshot_time]
+
+ if not snapshot_idx then
+ -- store snapshot if it doesn't exists
+ snapshot_idx = timestamps.n + 1
+ timestamps[snapshot_time] = snapshot_idx
+ timestamps.n = timestamps.n + 1
+ snapshot = Digraph.new {
+ syntactic_digraph = ugraph.syntactic_digraph,
+ options = ugraph.options
+ }
+ snapshot.timestamp = snapshot_time
+ snapshots[snapshot_idx] = snapshot
+ else
+ snapshot = snapshots[snapshot_idx]
+ end
+ assert(snapshot~=nil, "an unexpected error occurred")
+ return snapshot
+end
+
+
+---
+-- Generate a new supergraph to describe the whole evolving graph by
+-- collecting all temporal information from the digraph and the node
+-- options. All nodes in the |digraph| require a |snapshot| and
+-- a |supernode| option. To identify a (snapshot-)node with its
+-- supernode and snapshot.
+--
+-- @param digraph
+--
+-- @return The supergraph which is a |Digraph| that has a supervertex
+-- for each set of snapshot-vertices with the same |supernode|
+-- attribute.
+--
+function Supergraph.generateSupergraph(digraph)
+ local new_supergraph
+ new_supergraph = Supergraph.new {
+ syntactic_digraph = digraph.syntactic_digraph,
+ options = digraph.options,
+ digraph = digraph,
+ }
+
+ -- array to store the supervertices for a given vertex name
+ local local_snapshots = {} -- array to store each snapshot graphs
+
+ local timestamps = { n = 0 } -- set of snapshot times
+
+ -- separate and assign vertices to their snapshots and supervertices
+ for i,vertex in ipairs(digraph.vertices) do
+ local snapshot_time = assert(vertex.options["snapshot"], "Missing option 'snapshot' for vertex ".. vertex.name ..". ")
+ local supernode_name = assert(vertex.options["supernode"], "Missing option 'supernode' for vertex"..vertex.name..". ")
+
+ local snapshot = get_snapshot(local_snapshots, timestamps, digraph, snapshot_time)
+ local supervertex = new_supergraph.supervertices_by_id[supernode_name]
+
+ if not supervertex then
+ -- first appearance of the supernode id
+ supervertex = Vertex.new {
+ kind = "super",
+ name = supernode_name
+ }
+ supervertex.snapshots = {}
+ supervertex.subvertex = {}
+ new_supergraph.supervertices_by_id[supernode_name] = supervertex
+ new_supergraph:add{supervertex}
+
+ supervertex.options = {}
+ supervertex.options = vertex.options
+ end
+
+ snapshot:add{vertex}
+
+ new_supergraph.supervertices[vertex] = supervertex
+ new_supergraph.vertex_snapshots[vertex] = snapshot
+ new_supergraph:addSnapshotVertex(supervertex, snapshot, vertex)
+ end
+
+ -- Create edges
+ for i, e in ipairs(digraph.arcs) do
+ local u,v = e.tail, e.head
+ local snapshot_tail = new_supergraph.vertex_snapshots[e.tail]
+ local snapshot_head = new_supergraph.vertex_snapshots[e.head]
+
+ assert(snapshot_head == snapshot_tail, "Arcs must connect nodes that exist at the same time.")
+
+ -- connect in the snapshot graph
+ local arc = snapshot_tail:connect(u,v)
+
+ -- connect in the supergraph:
+ local super_tail = new_supergraph.supervertices[u]
+ local super_head = new_supergraph.supervertices[v]
+
+ new_supergraph:assignToSuperarc(super_tail, super_head, snapshot_tail)
+ end
+
+ -- snapshots in temporal order
+ table.sort(local_snapshots,
+ function(s1,s2)
+ return s1.timestamp < s2.timestamp
+ end )
+
+ local previous_snapshot
+
+ for i,s in ipairs(local_snapshots) do
+ local start = -math.huge
+ if previous_snapshot then
+ start = (s.timestamp - previous_snapshot.timestamp) / 2 + previous_snapshot.timestamp
+ previous_snapshot.interval.to = start
+ end
+ s.interval = { from = start , to = math.huge }
+ previous_snapshot = s
+ end
+
+ new_supergraph.snapshots = local_snapshots
+ new_supergraph.snapshots_indices = Storage.new()
+
+ for i, s in ipairs(new_supergraph.snapshots) do
+ new_supergraph.snapshots_indices[s] = i
+ end
+
+ return new_supergraph
+end
+
+
+function Supergraph:getSnapshotStaticDuration(snapshot)
+ assert(snapshot, "a snapshot as parameter expected, but got nil")
+ local idur = snapshot.interval.to - snapshot.interval.from
+ assert(idur, "unexpected nil-value")
+ local d1 = snapshot.interval.to - snapshot.timestamp
+ local d2 = snapshot.timestamp - snapshot.interval.from
+ local dm = math.min(d1,d2)
+ if (idur >= math.huge and dm < math.huge) then
+ return dm -- [-\infty,t] or [t,\infty]
+ elseif idur >= math.huge then
+ return 0 -- only one snapshot [-\infty,\infty]
+ else
+ return d1 + d2 -- [t_1, t_2]
+ end
+end
+
+---
+-- Get the durations of the graph in which snapshots are given which is exactly
+-- the time between the first and the last defined snapshot
+--
+-- @return The time between the last and first snapshot in seconds
+function Supergraph:getDuration()
+ local first_snapshot = self.snapshots[1]
+ local last_snapshot = self.snapshots[#self.snapshots]
+ return last_snapshot.timestamp - first_snapshot.timestamp
+end
+
+---
+--
+-- @return The ratio of the time of a snapshot related to the global duration of the whole
+-- evolving trees. (The time between the last and first snapshot)
+function Supergraph:getSnapshotRelativeDuration(snapshot)
+ if self:getDuration() == 0 then
+ return 1
+ else
+ return self:getSnapshotStaticDuration(snapshot) / self:getDuration()
+ end
+end
+
+---
+-- Give the supervertex for a certain pgf-vertex (vertex of a snapshot)
+--
+-- @param vertex A vertex of a snapshot.
+--
+-- @return A supervertex in the supergraph for the given vertex, nil if no
+-- supervertex was assigned before.
+--
+function Supergraph:getSupervertex(vertex)
+ assert(vertex, "vertex required")
+ assert(self.supervertices, "supervertex table is not defined")
+ return self.supervertices[vertex]
+end
+
+function Supergraph:getSuperarc(arc)
+ local superhead = self:getSupervertex(arc.head)
+ local supertail = self:getSupervertex(arc.tail)
+ local arc = assert(self:arc(supertail, superhead),"unexpected problem")
+ return arc
+end
+
+function Supergraph:getSnapshots(supervertex)
+ return supervertex.snapshots
+end
+
+---
+-- Find the snapshot-instance for a given pgf-vertex
+-- (which is a vertex for one certain snapshot)
+--
+-- @param vertex A vertex for which you want to get the related snapshot
+--
+-- @return The snapshot which contains the given vertex as vertex.
+function Supergraph:getSnapshot(vertex)
+ return self.vertex_snapshots[vertex]
+end
+
+---
+-- For a given supervertex get the related vertex for a snapshot
+--
+-- @param supervertex
+--
+-- @param snapshot
+--
+-- @return The vertex of the supervertex at the specified snapshot
+--
+function Supergraph:getSnapshotVertex(supervertex, snapshot)
+ assert(supervertex, "supervertex must not be nil")
+ assert(snapshot, "snapshot must not be nil")
+ return supervertex.subvertex[snapshot]
+end
+
+
+function Supergraph:consecutiveSnapshots(snapshot1, snapshot2, n)
+ assert(snapshot1 and snapshot2, "no snapshot passed")
+ local idx1 = self.snapshots_indices[snapshot1] --or -1
+ local idx2 = self.snapshots_indices[snapshot2] --or -1
+ local d = n or 1
+
+ return (idx2-idx1 <= d) or (idx1-idx2 <= d)
+end
+
+function Supergraph:consecutive(vertex1, vertex2, n)
+ local s1 = self:getSnapshot(vertex1)
+ local s2 = self:getSnapshot(vertex2)
+ return self:consecutiveSnapshots(s1, s2, n)
+end
+
+---
+-- Write pack all position information to the nodes of each snapshot
+-- such that all nodes with the same supervertex have the same position
+--
+-- @param ugraph An undirected graph for which the vertices should get
+-- their positions from the supergraph.
+--
+function Supergraph:sharePositions(ugraph, ignore)
+
+ for _,vertex in ipairs(ugraph.vertices) do
+ if not ignore then
+ vertex.pos.x = self.supervertices[vertex].pos.x
+ vertex.pos.y = self.supervertices[vertex].pos.y
+ else
+ if not ignore.x then
+ vertex.pos.x = self.supervertices[vertex].pos.x
+ end
+ if not ignore.y then
+ vertex.pos.y = self.supervertices[vertex].pos.y
+ end
+ end
+
+
+ end
+end
+
+function Supergraph:onAllSnapshotvertices(f, ugraph)
+ for _,vertex in ipairs(ugraph.vertices) do
+ local snapshot_vertex = self.supertvertices[vertex]
+ if snapshot_vertex then
+ f(vertex, snapshot_vertex)
+ end
+ end
+end
+
+---
+-- Split a supervertex into new supervertices such that
+-- for a given snapshot there is a new pseudo-supervertex.
+-- This pseudo-supervertex will be assigned to all snapshots
+-- after the given snapshot.
+-- All snapshots of a new pseudo-supervertex are removed from
+-- the original vertex.
+-- If a supervertex has no subvertices then it will not be added to the graph.
+--
+-- @param supervertex The supervertex which should be split.
+--
+-- @param snapshots An array of snapshots at which the supervertex
+-- should be split into a new one with the corresponding pgf-vertices.
+-- If there are more than one snapshots passed to the function
+-- for each snapshot there will be a new pseudo-vertex
+--
+function Supergraph:splitSupervertex(supervertex, snapshots)
+ assert(supervertex, "no supervertex defined")
+ -- snapshots in temporal order
+ table.sort(snapshots,
+ function(s1,s2)
+ return s1.timestamp < s2.timestamp
+ end )
+
+ assert(#snapshots~=0)
+
+ local edit_snapshots = supervertex.snapshots
+ local first_removed = math.huge
+ local rem_arcs = {}
+ for i = 1, #snapshots do
+ local s_first = self.snapshots_indices[snapshots[i]]
+ first_removed = math.min(s_first,first_removed)
+ local s_last
+ if i==#snapshots then
+ s_last = #self.snapshots
+ else
+ s_last = self.snapshots_indices[snapshots[i+1]]-1
+ end
+
+ local pseudovertex = Vertex.new {
+ kind = "super",
+ name = supervertex.name.."*"..i,
+ subvertex = {},
+ snapshots = {}
+ }
+
+ local has_subvertices = false
+
+ for j = s_first, s_last do
+ local s = self.snapshots[j]
+ local vertex = self:getSnapshotVertex(supervertex, s)
+ if vertex then
+ self.supervertices[vertex] = pseudovertex
+ self:addSnapshotVertex(pseudovertex, s, vertex)
+ self:removeSnapshotVertex(supervertex, s)
+
+ if not has_subvertices then
+ has_subvertices = true
+ self:add{pseudovertex}
+ end
+
+ -- update edges:
+ local incoming = self.digraph:incoming(vertex)
+ local outgoing = self.digraph:outgoing(vertex)
+
+ for _, arc in ipairs(incoming) do
+ local tail = self.supervertices[arc.tail]
+ local head = self.supervertices[arc.head]
+ self:assignToSuperarc(tail, pseudovertex, s)
+
+ local super_arc = self:arc(tail, supervertex)
+ if not rem_arcs[super_arc] then
+ table.insert(rem_arcs, {arc = super_arc, snapshot = s})
+ rem_arcs[super_arc] = true
+ end
+ end
+
+ for _, arc in ipairs(outgoing) do
+ local tail = self.supervertices[arc.tail]
+ local head = self.supervertices[arc.head]
+ self:assignToSuperarc(pseudovertex, head, s)
+
+ local super_arc = self:arc(supervertex, head)
+ if not rem_arcs[super_arc] then
+ table.insert(rem_arcs, {arc = super_arc, snapshot = s})
+ rem_arcs[super_arc] = true
+ end
+ end
+ end
+ end
+ end
+
+ if first_removed ~= math.huge then
+ for _, removed_arc in ipairs(rem_arcs) do
+ local snapshots = self.arc_snapshots[removed_arc.arc]
+ for i=#snapshots,1,-1 do
+ local s = snapshots[i]
+ if s.timestamp >= removed_arc.snapshot.timestamp then
+ table.remove(snapshots, i)
+ end
+ end
+
+ if #snapshots==0 then
+ self:disconnect(removed_arc.arc.tail, removed_arc.arc.head)
+ end
+ end
+ end
+end
+
+-- function Supergraph:reloadArcSnapshots()
+-- for _, arc in ipairs(self.digraph.arcs) do
+-- local snapshot = self:getSnapshot(arc.head)
+-- local superarc = self:getSuperarc(arc)
+-- texio.write("\n"..arc.tail.name..">"..arc.head.name)
+-- self.arc_snapshots[superarc] = snapshot
+-- end
+-- end
+
+---
+-- Remove the binding of a vertex at a certain snapshot from its assigned
+-- supervertex.
+-- This requires time $O(n)$ where $n$ is the number of nodes actually
+-- assigned to the supervertex.
+function Supergraph:removeSnapshotVertex(supervertex, snapshot)
+ assert(supervertex and snapshot,"missing argument: the supervertex and snapshot must not be nil")
+
+ -- remove reference to snapshot
+ for i = #supervertex.snapshots,1,-1 do
+ if supervertex.snapshots[i] == snapshot then
+ table.remove(supervertex.snapshots, i)
+ end
+ end
+ -- remove vertex at snapshot
+ supervertex.subvertex[snapshot] = nil
+end
+
+---
+-- Assign a vertex to a snapshot vertex of this supergraph.
+-- This requires time $O(1)$
+-- @param supervertex
+--
+-- @param snapshot
+--
+-- @param vertex The vertex which should be assigned to the supervertex
+-- for the given snapshot.
+--
+function Supergraph:addSnapshotVertex(supervertex, snapshot, vertex)
+ supervertex.subvertex[snapshot] = vertex
+ table.insert(supervertex.snapshots, snapshot)
+end
+
+---
+-- Assign a given snapshot to the superarc between two supernodes.
+-- If still no arc between those nodes exists a new edges will
+-- be created.
+-- This requires time $O(n)$ where $n$ is the number of snapshots already
+-- assigned to the given arc.
+--
+-- @param super_tail The tail of the directed arc in the supergraph.
+--
+-- @param super_head The head of the directed arc in the supergraph.
+--
+-- @param snapshot A snapshot in which both nodes are connected.
+--
+-- @return The arc which was created or updated.
+--
+function Supergraph:assignToSuperarc(super_tail, super_head, snapshot)
+ assert(self:contains(super_tail) and self:contains(super_head),
+ "tried to connect supernodes not in the supergraph")
+
+ local super_arc = self:arc(super_tail, super_head)
+ if not super_arc then
+ super_arc = self:connect(super_tail, super_head)
+ end
+
+ table.insert(self.arc_snapshots[super_arc], snapshot)
+ self.arc_snapshots[super_arc][snapshot] = true
+
+ return super_arc
+end
+
+return Supergraph
+
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/experimental/evolving/SupergraphVertexSplitOptimization.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/experimental/evolving/SupergraphVertexSplitOptimization.lua
new file mode 100644
index 0000000000..b5a4ea22c9
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/experimental/evolving/SupergraphVertexSplitOptimization.lua
@@ -0,0 +1,196 @@
+-- Copyright 2015 by Malte Skambath
+--
+-- 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
+
+
+local SupergraphVertexSplitOptimization = {}
+
+-- Imports
+local lib = require "pgf.gd.lib"
+local declare = require("pgf.gd.interface.InterfaceToAlgorithms").declare
+
+local Vertex = require "pgf.gd.model.Vertex"
+local Digraph = require "pgf.gd.model.Digraph"
+local Coordinate = require "pgf.gd.model.Coordinate"
+
+declare {
+ key = "split me",
+ type = "boolean",
+ initial = false
+}
+
+declare {
+ key = "split on disappearing",
+ type = "boolean",
+ initial = true
+}
+
+declare {
+ key = "split on disjoint neighbors",
+ type = "boolean",
+ initial = false
+}
+
+declare {
+ key = "split on disjoint children",
+ type = "boolean",
+ initial = false
+}
+
+declare {
+ key = "split on disjoint parents",
+ type = "boolean",
+ initial = false
+}
+
+declare {
+ key = "split all supervertices",
+ type = "boolean",
+ initial = false
+}
+
+declare {
+ key = "unbound vertex splitting",
+ algorithm = SupergraphVertexSplitOptimization,
+ phase = "supergraph optimization",
+ phase_default = true,
+ summary = [["
+ Use this key if you want to disable animations.
+ Instead of producing animations the evolving graph animation phasephase animates all vertices including movements and
+ fade in or fade out animations.
+ "]],
+ documentation = [["
+ See ToDo
+ "]]
+}
+
+
+
+-- Help functions
+
+
+-- Implementation
+
+function SupergraphVertexSplitOptimization:run()
+ local supergraph = assert(self.supergraph, "no supergraph passed")
+
+ local split_on_dissapearing = self.digraph.options["split on disappearing"]
+ local split_on_no_common_neighbor = self.digraph.options["split on disjoint neighbors"]
+ local split_on_no_common_child = self.digraph.options["split on disjoint children"]
+ local split_on_no_common_parent = self.digraph.options["split on disjoint parents"]
+ local split_all = self.digraph.options["split all supervertices"]
+
+ for _, supernode in ipairs(supergraph.vertices) do
+ -- follow trace of the supernode
+ local snapshots = supergraph:getSnapshots(supernode)
+ local splitsnapshots = {}
+
+ for i=2, #snapshots do
+ local s = snapshots[i]
+ local s_prev = snapshots[i - 1]
+ local can_split = false
+
+ if supergraph:consecutiveSnapshots(s_prev, s) then
+ local v1 = supergraph:getSnapshotVertex(supernode, s_prev)
+ local v2 = supergraph:getSnapshotVertex(supernode, s)
+ local is_child1 = {}
+ local is_parent1 = {}
+ local is_neighbor1 = {}
+
+ local incoming1 = s_prev:incoming(v1)
+ local outgoing1 = s_prev:outgoing(v1)
+
+ for _,e in ipairs(incoming1) do
+ local p = supergraph:getSupervertex(e.tail)
+ if p then
+ is_parent1[p] = true
+ is_neighbor1[p] = true
+ end
+ end
+
+ for _,e in ipairs(outgoing1) do
+ local p = supergraph:getSupervertex(e.head)
+ if p then
+ is_child1[p] = true
+ is_neighbor1[p] = true
+ end
+ end
+
+ local incoming2 = s:incoming(v2)
+ local outgoing2 = s:outgoing(v2)
+
+ no_common_parent = true
+ no_common_child = true
+ no_common_neighbor = true
+ for _,e in ipairs(incoming2) do
+ local p = supergraph:getSupervertex(e.tail)
+ if p then
+ if is_neighbor1[p] then
+ no_common_neighbor = false
+ end
+ if is_parent1[p] then
+ no_common_parent = false
+ end
+ if (not no_common_neighbor) and (not no_common_parent) then
+ break
+ end
+ end
+ end
+
+ for _,e in ipairs(outgoing2) do
+ local p = supergraph:getSupervertex(e.head)
+ if p then
+ if is_neighbor1[p] then
+ no_common_neighbor = false
+ end
+ if is_child1[p] then
+ no_common_child = false
+ end
+ if (not no_common_neighbor) and (not no_common_child) then
+ break
+ end
+ end
+ end
+
+
+
+ if no_common_neighbor and split_on_no_common_neighbor then
+ can_split = true
+ --texio.write("[N@".. s.timestamp .."]")
+ end
+ if no_common_parent and split_on_no_common_parent then
+ can_split = true
+ --texio.write("[P@".. s.timestamp .."]")
+ end
+ if no_common_child and split_on_no_common_child then
+ can_split = true
+ --texio.write("[N@".. s.timestamp .."]")
+ end
+ if v2.options["split me"] then
+ can_split = true
+ end
+ else
+ can_split = true
+ --texio.write("[R@".. s.timestamp .."]")
+ end
+ if can_split or split_all then
+ table.insert(splitsnapshots, s)
+ end
+ end
+ if #splitsnapshots>0 then
+ supergraph:splitSupervertex(supernode, splitsnapshots)
+ end
+ end
+end
+
+
+
+
+-- Done
+
+return SupergraphVertexSplitOptimization
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/experimental/evolving/TimeSpec.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/experimental/evolving/TimeSpec.lua
new file mode 100644
index 0000000000..2b2168da1a
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/experimental/evolving/TimeSpec.lua
@@ -0,0 +1,62 @@
+-- Copyright 2015 by Malte Skambath
+--
+-- 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 declare = require("pgf.gd.interface.InterfaceToAlgorithms").declare
+
+---
+
+declare {
+ key = "snapshot",
+ type = "time",
+ initial = "0s",
+ summary = "The time of the snapshot in which a PGF node should be visible.",
+ documentation = [["
+ This option defines the time in seconds when respectively in which
+ state or snapshot of the graph the PGF represents a graph node.
+ "]],
+}
+
+---
+
+declare {
+ key = "supernode",
+ type = "string",
+ initial = "null",
+ summary = "A unique name for a node a given PGF node should be assigned to.",
+ documentation = [["
+ Because it should be possible that nodes can change their
+ appearance, they are represented by separate PGF nodes in each
+ snapshot. To identify PGF nodes of the same supernode we have to
+ specify this key.
+ "]],
+}
+
+---
+
+declare {
+ key = "fadein time",
+ type = "time",
+ initial = "0.5s",
+ summary = [["
+ The time in seconds it should take that a nodes will be fade in
+ when it disappears in the graph.
+ "]],
+}
+
+---
+
+declare {
+ key = "fadeout time",
+ type = "time",
+ initial = "0.5s",
+ summary = "",
+ documentation = "The same as |fadein time| but for disappearing nodes.",
+}
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/experimental/evolving/doc.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/experimental/evolving/doc.lua
new file mode 100644
index 0000000000..dbd8146275
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/experimental/evolving/doc.lua
@@ -0,0 +1,120 @@
+-- Copyright 2012 by Malte Skambath
+--
+-- 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
+
+--
+
+
+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 "animated tree layout"
+
+summary "This layout uses the Reingold--Tilform method for drawing trees."
+
+documentation
+[[
+A method to create layouts for evolving graphs as an SVG animation.The Reingold--Tilford method is a standard method for drawing
+trees. It is described in:
+
+The algorithm, which is based on the Reingold--Tilford algorithm and
+its implementation in |graphdrawing.trees|, is introduced in my Masthesis:
+%
+\begin{itemize}
+ \item
+ M.\ Skambath,
+ \newblock Algorithmic Drawing of Evolving Trees, Masterthesis, 2016
+\end{itemize}
+
+You can use the same known graph macros as for other graph drawing
+algorithms in Ti\emph{k}Z. In addition all keys and features that
+are available for the static tree algorithm can be used:
+%
+\begin{codeexample}[animation list={1,1.5,2,2.5,3,3.5,4}]
+ \tikz \graph[animated binary tree layout,
+ nodes={draw,circle}, auto supernode,
+ ] {
+ {[when=1] 15 -> {10 -> { ,11}, 20 }},
+ {[when=2] 15 -> {10 -> {3,11}, 20 }},
+ {[when=3] 15 -> {10 -> {3, }, 20 }},
+ {[when=4] 15 -> {10 -> {3, }, 20 -> 18 }},
+ };
+\end{codeexample}
+]]
+
+
+example
+[[
+\tikz[animated binary tree layout]
+ \graph[nodes={draw,circle}, auto supernode] {
+ {[when=1] 15 -> {10 -> { ,11}, 20 }},
+ {[when=2] 15 -> {10 -> {3,11}, 20 }},
+ {[when=3] 15 -> {10 -> {3, }, 20 }},
+ {[when=4] 15 -> {10 -> {3, }, 20 -> 18 }},
+ };
+]]
+--------------------------------------------------------------------
+
+
+
+--------------------------------------------------------------------
+
+--------------------------------------------------------------------
+
+
+
+--------------------------------------------------------------------
+key "animated binary tree layout"
+
+summary
+[[ A layout based on the Reingold--Tilford method for drawing
+binary trees.
+]]
+
+documentation
+[[
+This key executes:
+%
+\begin{enumerate}
+ \item |animated tree layout|, thereby selecting the Reingold--Tilford method,
+ \item |minimum number of children=2|, thereby ensuring the all nodes
+ have (at least) two children or none at all, and
+\end{enumerate}
+]]
+
+
+example
+[[
+]]
+
+example
+[[
+]]
+--------------------------------------------------------------------
+
+
+
+--------------------------------------------------------------------
+key "extended animated binary tree layout"
+
+summary
+[[ This algorithm is similar to |animated binary tree layout|, only the
+option \texttt{missing nodes get space} is executed and the
+\texttt{significant sep} is zero.
+]]
+
+example
+[[
+]]
+--------------------------------------------------------------------
+
+
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/experimental/evolving/layered.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/experimental/evolving/layered.lua
new file mode 100644
index 0000000000..15df9990f8
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/experimental/evolving/layered.lua
@@ -0,0 +1,107 @@
+-- Copyright 2012 by Till Tantau
+-- Copyright 2015 by Malte Skambath
+--
+-- 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
+
+
+
+
+local temporallayered = {}
+
+-- Namespace
+
+--require("pgf.gd").layered = layered
+--require("pgf.gd.experimental.evolving").layered = layered
+
+-- Import
+local lib = require "pgf.gd.lib"
+local Storage = require "pgf.gd.lib.Storage"
+local layered = require "pgf.gd.layered"
+
+--
+-- This file defines some basic functions to compute and/or set the
+-- ideal distances between nodes of any kind of layered drawing of a
+-- graph.
+
+
+
+---
+-- Position nodes in layers using baselines
+--
+-- @param layers A |Storage| object assigning layers to vertices.
+-- @param paddings A |Storage| object storing the computed distances
+-- (paddings).
+-- @param graph The graph in which the nodes reside
+-- @param snapshots The list of snapshots over which the overlaying evolving
+-- graph exists
+function temporallayered.arrange_layers_by_baselines (layers, paddings, graph, snapshots, vertex_snapshots)
+ assert(vertex_snapshots, "vertex_snapshots must not be nil")
+ --local layer_vertices = Storage.newTableStorage()
+ local snapshots_layers = Storage.newTableStorage()
+ local count_layers = 0
+ -- Decompose into layers:
+ for _,v in ipairs(graph.vertices) do
+ local layer_vertices = snapshots_layers[vertex_snapshots[v]] or {}
+ if layer_vertices[layers[v]] == nil then
+ assert( layers[v], "layer of node " .. v.name .. " has not been computed.")
+ layer_vertices[layers[v]] = {}
+ end
+ table.insert(layer_vertices[layers[v]], v)
+ count_layers = math.max(count_layers, layers[v])
+ end
+
+ if count_layers > 0 then
+
+
+ -- Now compute ideal distances and store
+ local height = 0
+
+ for _, s in ipairs(snapshots) do
+ local layer_vertices = snapshots_layers[s]
+ if #layer_vertices > 0 then -- sanity check
+ for _,v in ipairs(layer_vertices[1]) do
+ v.pos.y = 0
+ end
+ end
+ end
+
+ for i=2, count_layers do
+ local distance = 0
+ for _, s in ipairs(snapshots) do
+ local layer_vertices = snapshots_layers[s]
+ if #layer_vertices >= i then
+ distance = math.max(
+ distance,
+ layered.baseline_distance(
+ paddings,
+ s,
+ layer_vertices[i-1],
+ layer_vertices[i]))
+ end
+ end
+
+ height = height + distance
+
+ for _, s in ipairs(snapshots) do
+ local layer_vertices = snapshots_layers[s]
+ if #layer_vertices >= i then
+ for _,v in ipairs(layer_vertices[i]) do
+ v.pos.y = height
+ end
+ end
+ end
+ end
+ end
+end
+
+
+
+
+-- Done
+
+return temporallayered
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/experimental/evolving/library.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/experimental/evolving/library.lua
new file mode 100644
index 0000000000..bb50eaf2cb
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/experimental/evolving/library.lua
@@ -0,0 +1,33 @@
+-- Copyright 2016 by Malte Skambath
+--
+-- 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
+
+
+
+-- @library
+
+local evolving -- Library name
+
+-- Load dependencies:
+require "pgf.gd.trees.ChildSpec"
+require "pgf.gd.trees.ReingoldTilford1981"
+require "pgf.gd.layered"
+
+-- Load declarations from:
+require "pgf.gd.experimental.evolving.TimeSpec"
+require "pgf.gd.experimental.evolving.Supergraph"
+
+-- Load preprocessing/optimization phases from:
+require "pgf.gd.experimental.evolving.SupergraphVertexSplitOptimization"
+require "pgf.gd.experimental.evolving.GreedyTemporalCycleRemoval"
+
+-- Load postprocessing/graph animation phases from:
+require "pgf.gd.experimental.evolving.GraphAnimationCoordination"
+
+-- Load algorithms from:
+require "pgf.gd.experimental.evolving.Skambath2016"
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/force.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/force.lua
new file mode 100644
index 0000000000..71efda1b35
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/force.lua
@@ -0,0 +1,26 @@
+-- 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$
+
+
+
+-- Imports
+
+require "pgf"
+require "pgf.gd"
+
+
+-- Declare namespace
+pgf.gd.force = {}
+
+
+-- Done
+
+return pgf.gd.force \ No newline at end of file
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/force/CoarseGraph.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/force/CoarseGraph.lua
new file mode 100644
index 0000000000..a4e51dfeff
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/force/CoarseGraph.lua
@@ -0,0 +1,435 @@
+-- Copyright 2011 by Jannis Pohlmann
+-- 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$
+
+
+--- A class for handling "coarse" versions of a graph. Such versions contain
+-- less nodes and edges than the original graph while retaining the overall
+-- structure.
+
+local Graph = require "pgf.gd.deprecated.Graph" -- we subclass from here
+local CoarseGraph = Graph.new()
+CoarseGraph.__index = CoarseGraph
+
+
+
+-- Namespace:
+local force = require "pgf.gd.force"
+force.CoarseGraph = CoarseGraph
+
+
+-- Imports
+local Node = require "pgf.gd.deprecated.Node"
+local Edge = require "pgf.gd.deprecated.Edge"
+
+local lib = require "pgf.gd.lib"
+
+
+-- Class setup
+
+CoarseGraph.COARSEN_INDEPENDENT_EDGES = 0 -- TT: Remark: These uppercase constants are *ugly*. Why do people do this?!
+CoarseGraph.COARSEN_INDEPENDENT_NODES = 1
+CoarseGraph.COARSEN_HYBRID = 2
+
+
+
+--- Creates a new coarse graph derived from an existing graph.
+--
+-- Generates a coarse graph for the input |Graph|.
+--
+-- Coarsening describes the process of reducing the amount of nodes in a graph
+-- by merging nodes into supernodes. There are different strategies, called
+-- schemes, that can be applied, like merging nodes that belong to edges in a
+-- maximal independent edge set or by creating supernodes based on a maximal
+-- independent node set.
+--
+-- Coarsening is not performed automatically. The functions |CoarseGraph:coarsen|
+-- and |CoarseGraph:interpolate| can be used to further coarsen the graph or
+-- to restore the previous state (while interpolating the node positions from
+-- the coarser version of the graph).
+--
+-- 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{graph} needs to be passed over
+-- to |CoarseGraph.new|.
+--
+-- @param graph An existing graph that needs to be coarsened.
+-- @param scheme Coarsening scheme to use. Possible values are:\par
+-- |CoarseGraph.COARSEN_INDEPENDENT_EDGES|:
+-- Coarsen the input graph by computing a maximal independent edge set
+-- and collapsing edges from this set. The resulting coarse graph has
+-- at least 50% of the nodes of the input graph. This coarsening scheme
+-- gives slightly better results than
+-- |CoarseGraph.COARSEN_INDEPENDENT_NODES| because it is less aggressive.
+-- However, this comes at higher computational cost.\par
+-- |CoarseGraph.COARSEN_INDEPENDENT_NODES|:
+-- Coarsen the input graph by computing a maximal independent node set,
+-- making nodes from this set supernodes in the coarse graph, merging
+-- adjacent nodes into the supernodes and connecting the supernodes
+-- if their graph distance is no greater than three. This scheme gives
+-- slightly worse results than |CoarseGraph.COARSEN_INDEPENDENT_EDGES|
+-- but is computationally more efficient.\par
+-- |CoarseGraph.COARSEN_HYBRID|: Combines the other schemes by starting
+-- with |CoarseGraph.COARSEN_INDEPENDENT_EDGES| and switching to
+-- |CoarseGraph.COARSEN_INDEPENDENT_NODES| as soon as the first scheme
+-- does not reduce the amount of nodes by a factor of 25%.
+--
+function CoarseGraph.new(graph, scheme)
+ local coarse_graph = {
+ graph = graph,
+ level = 0,
+ scheme = scheme or CoarseGraph.COARSEN_INDEPENDENT_EDGES,
+ ratio = 0,
+ }
+ setmetatable(coarse_graph, CoarseGraph)
+ return coarse_graph
+end
+
+
+
+local function custom_merge(table1, table2, first_metatable)
+ local result = table1 and lib.copy(table1) or {}
+ local first_metatable = first_metatable == true or false
+
+ for key, value in pairs(table2) do
+ if not result[key] then
+ result[key] = value
+ end
+ end
+
+ if not first_metatable or not getmetatable(result) then
+ setmetatable(result, getmetatable(table2))
+ end
+
+ return result
+end
+
+
+local function pairs_by_sorted_keys (t, f)
+ local a = {}
+ for n in pairs(t) do a[#a + 1] = n end
+ table.sort (a, f)
+ local i = 0
+ return function ()
+ i = i + 1
+ return a[i], t[a[i]]
+ end
+end
+
+
+
+function CoarseGraph:coarsen()
+ -- update the level
+ self.level = self.level + 1
+
+ local old_graph_size = #self.graph.nodes
+
+ if self.scheme == CoarseGraph.COARSEN_INDEPENDENT_EDGES then
+ local matching, unmatched_nodes = self:findMaximalMatching()
+
+ for _,edge in ipairs(matching) do
+ -- get the two nodes of the edge that we are about to collapse
+ local u, v = edge.nodes[1], edge.nodes[2]
+
+ assert(u ~= v, 'the edge ' .. tostring(edge) .. ' is a loop. loops are not supported by this algorithm')
+
+ -- create a supernode
+ local supernode = Node.new{
+ name = '(' .. u.name .. ':' .. v.name .. ')',
+ weight = u.weight + v.weight,
+ subnodes = { u, v },
+ subnode_edge = edge,
+ level = self.level,
+ }
+
+ -- add the supernode to the graph
+ self.graph:addNode(supernode)
+
+ -- collect all neighbors of the nodes to merge, create a node -> edge mapping
+ local u_neighbours = lib.map(u.edges, function(edge) return edge, edge:getNeighbour(u) end)
+ local v_neighbours = lib.map(v.edges, function(edge) return edge, edge:getNeighbour(v) end)
+
+ -- remove the two nodes themselves from the neighbor lists
+ u_neighbours = lib.map(u_neighbours, function (edge,node) if node ~= v then return edge,node end end)
+ v_neighbours = lib.map(v_neighbours, function (edge,node) if node ~= u then return edge,node end end)
+
+ -- compute a list of neighbors u and v have in common
+ local common_neighbours = lib.map(u_neighbours,
+ function (edge,node)
+ if v_neighbours[node] ~= nil then return edge,node end
+ end)
+
+ -- create a node -> edges mapping for common neighbors
+ common_neighbours = lib.map(common_neighbours, function (edge, node)
+ return { edge, v_neighbours[node] }, node
+ end)
+
+ -- drop common edges from the neighbor mappings
+ u_neighbours = lib.map(u_neighbours, function (val,node) if not common_neighbours[node] then return val,node end end)
+ v_neighbours = lib.map(v_neighbours, function (val,node) if not common_neighbours[node] then return val,node end end)
+
+ -- merge neighbor lists
+ local disjoint_neighbours = custom_merge(u_neighbours, v_neighbours)
+
+ -- create edges between the supernode and the neighbors of the merged nodes
+ for neighbour, edge in pairs_by_sorted_keys(disjoint_neighbours, function (n,m) return n.index < m.index end) do
+
+ -- create a superedge to replace the existing one
+ local superedge = Edge.new{
+ direction = edge.direction,
+ weight = edge.weight,
+ subedges = { edge },
+ level = self.level,
+ }
+
+ -- add the supernode and the neighbor to the edge
+ if u_neighbours[neighbour] then
+ superedge:addNode(neighbour)
+ superedge:addNode(supernode)
+
+ else
+ superedge:addNode(supernode)
+ superedge:addNode(neighbour)
+
+ end
+
+ -- replace the old edge
+ self.graph:addEdge(superedge)
+ self.graph:deleteEdge(edge)
+ end
+
+ -- do the same for all neighbors that the merged nodes have
+ -- in common, except that the weights of the new edges are the
+ -- sums of the of the weights of the edges to the common neighbors
+ for neighbour, edges in pairs_by_sorted_keys(common_neighbours, function (n,m) return n.index < m.index end) do
+ local weights = 0
+ for _,e in ipairs(edges) do
+ weights = weights + edge.weight
+ end
+
+ local superedge = Edge.new{
+ direction = Edge.UNDIRECTED,
+ weight = weights,
+ subedges = edges,
+ level = self.level,
+ }
+
+ -- add the supernode and the neighbor to the edge
+ superedge:addNode(supernode)
+ superedge:addNode(neighbour)
+
+ -- replace the old edges
+ self.graph:addEdge(superedge)
+ for _,edge in ipairs(edges) do
+ self.graph:deleteEdge(edge)
+ end
+ end
+
+ -- delete the nodes u and v which were replaced by the supernode
+ assert(#u.edges == 1, 'node ' .. u.name .. ' is part of a multiedge') -- if this fails, then there is a multiedge involving u
+ assert(#v.edges == 1, 'node ' .. v.name .. ' is part of a multiedge') -- same here
+ self.graph:deleteNode(u)
+ self.graph:deleteNode(v)
+ end
+ 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 = #self.graph.nodes / old_graph_size
+end
+
+
+
+function CoarseGraph:revertSuperedge(superedge)
+ -- TODO we can probably skip adding edges that have one or more
+ -- subedges with the same level. But that needs more testing.
+
+ -- TODO we might have to pass the corresponding supernode to
+ -- this method so that we can move subnodes to the same
+ -- position, right? Interpolating seems to work fine without
+ -- though...
+
+ if #superedge.subedges == 1 then
+ local subedge = superedge.subedges[1]
+
+ if not self.graph:findNode(subedge.nodes[1].name) then
+ self.graph:addNode(subedge.nodes[1])
+ end
+
+ if not self.graph:findNode(subedge.nodes[2].name) then
+ self.graph:addNode(subedge.nodes[2])
+ end
+
+ if not self.graph:findEdge(subedge) then
+ subedge.nodes[1]:addEdge(subedge)
+ subedge.nodes[2]:addEdge(subedge)
+ self.graph:addEdge(subedge)
+ end
+
+ if subedge.level and subedge.level >= self.level then
+ self:revertSuperedge(subedge)
+ end
+ else
+ for _,subedge in ipairs(superedge.subedges) do
+ if not self.graph:findNode(subedge.nodes[1].name) then
+ self.graph:addNode(subedge.nodes[1])
+ end
+
+ if not self.graph:findNode(subedge.nodes[2].name) then
+ self.graph:addNode(subedge.nodes[2])
+ end
+
+ if not self.graph:findEdge(subedge) then
+ subedge.nodes[1]:addEdge(subedge)
+ subedge.nodes[2]:addEdge(subedge)
+ self.graph:addEdge(subedge)
+ end
+
+ if subedge.level and subedge.level >= self.level then
+ self:revertSuperedge(subedge)
+ end
+ end
+ end
+end
+
+
+
+function CoarseGraph:interpolate()
+ -- FIXME TODO Jannis: This does not work now that we allow multi-edges
+ -- and loops! Reverting generates the same edges multiple times which leads
+ -- to distorted drawings compared to the awesome results we had before!
+
+ local nodes = lib.copy(self.graph.nodes)
+
+ for _,supernode in ipairs(nodes) do
+ assert(not supernode.level or supernode.level <= self.level)
+
+ if supernode.level and supernode.level == self.level then
+ -- move the subnode to the position of the supernode and add it to the graph
+ supernode.subnodes[1].pos.x = supernode.pos.x
+ supernode.subnodes[1].pos.y = supernode.pos.y
+
+ if not self.graph:findNode(supernode.subnodes[1].name) then
+ self.graph:addNode(supernode.subnodes[1])
+ end
+
+ -- move the subnode to the position of the supernode and add it to the graph
+ supernode.subnodes[2].pos.x = supernode.pos.x
+ supernode.subnodes[2].pos.y = supernode.pos.y
+
+ if not self.graph:findNode(supernode.subnodes[2].name) then
+ self.graph:addNode(supernode.subnodes[2])
+ end
+
+ if not self.graph:findEdge(supernode.subnode_edge) then
+ supernode.subnodes[1]:addEdge(supernode.subnode_edge)
+ supernode.subnodes[2]:addEdge(supernode.subnode_edge)
+ self.graph:addEdge(supernode.subnode_edge)
+ end
+
+ local superedges = lib.copy(supernode.edges)
+
+ for _,superedge in ipairs(superedges) do
+ self:revertSuperedge(superedge)
+ end
+
+ self.graph:deleteNode(supernode)
+ end
+ end
+
+ -- Make sure that the nodes and edges are in the correct order:
+ table.sort (self.graph.nodes, function (a, b) return a.index < b.index end)
+ table.sort (self.graph.edges, function (a, b) return a.index < b.index end)
+ for _, n in pairs(self.graph.nodes) do
+ table.sort (n.edges, function (a, b) return a.index < b.index end)
+ end
+
+ -- update the level
+ self.level = self.level - 1
+end
+
+
+
+function CoarseGraph:getSize()
+ return #self.graph.nodes
+end
+
+
+
+function CoarseGraph:getRatio()
+ return self.ratio
+end
+
+
+
+function CoarseGraph:getLevel()
+ return self.level
+end
+
+
+
+function CoarseGraph:getGraph()
+ return self.graph
+end
+
+
+
+function CoarseGraph:findMaximalMatching()
+ local matching = {}
+ local matched_nodes = {}
+ local unmatched_nodes = {}
+
+ -- iterate over nodes in random order
+ for _,j in ipairs(lib.random_permutation(#self.graph.nodes)) do
+ local node = self.graph.nodes[j]
+ -- ignore nodes that have already been matched
+ if not matched_nodes[node] then
+ -- mark the node as matched
+ matched_nodes[node] = true
+
+ -- filter out edges adjacent to neighbors already matched
+ local edges = lib.imap(node.edges,
+ function (edge)
+ if not matched_nodes[edge:getNeighbour(node)] then return edge end
+ end)
+
+ -- FIXME TODO We use a light-vertex matching here. This is
+ -- different from the algorithm proposed by Hu which collapses
+ -- edges based on a heavy-edge matching...
+ if #edges > 0 then
+ -- sort edges by the weights of the node's neighbors
+ table.sort(edges, function (a, b)
+ return a:getNeighbour(node).weight < b:getNeighbour(node).weight
+ end)
+
+ -- match the node against the neighbor with minimum weight
+ matched_nodes[edges[1]:getNeighbour(node)] = true
+ table.insert(matching, edges[1])
+ end
+ end
+ end
+
+ -- generate a list of nodes that were not matched at all
+ for _,j in ipairs(lib.random_permutation(#self.graph.nodes)) do
+ local node = self.graph.nodes[j]
+ if not matched_nodes[node] then
+ table.insert(unmatched_nodes, node)
+ end
+ end
+
+ return matching, unmatched_nodes
+end
+
+
+-- done
+
+return CoarseGraph
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/force/ControlCoarsening.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/force/ControlCoarsening.lua
new file mode 100644
index 0000000000..d69d5fc6c9
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/force/ControlCoarsening.lua
@@ -0,0 +1,148 @@
+-- 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$
+
+
+-- Imports
+local declare = require("pgf.gd.interface.InterfaceToAlgorithms").declare
+
+
+
+---
+-- @section subsubsection {Coarsening}
+--
+-- @end
+
+
+---
+
+declare {
+ key = "coarsen",
+ type = "boolean",
+ initial = "true",
+
+ summary = [["
+ Defines whether or not a multilevel approach is used that
+ iteratively coarsens the input graph into graphs $G_1,\dots,G_l$
+ with a smaller and smaller number of nodes. The coarsening stops as
+ soon as a minimum number of nodes is reached, as set via the
+ |minimum coarsening size| option, or if, in the last iteration, the
+ number of nodes was not reduced by at least the ratio specified via
+ |downsize ratio|.
+ "]],
+ documentation = [["
+ A random initial layout is computed for the coarsest graph $G_l$ first.
+ Afterwards, it is laid out by computing the attractive and repulsive
+ forces between its nodes.
+
+ In the subsequent steps, the previous coarse graph $G_{l-1}$ is
+ restored and its node positions are interpolated from the nodes
+ in~$G_l$. The graph $G_{l-1}$ is again laid out by computing the forces
+ between its nodes. These steps are repeated with $G_{l-2},\dots,G_1$ until
+ the original input graph $G_0$ has been restored, interpolated
+ and laid out.
+
+ The idea behind this approach is that, by arranging recursively
+ formed supernodes first and then interpolating and arranging their
+ subnodes step by step, the algorithm is less likely to settle in a
+ local energy minimum (of which there can be many, particularly for
+ large graphs). The quality of the drawings with coarsening enabled is
+ expected to be higher than graphics where this feature is not applied.
+
+ The following example demonstrates how coarsening can improve the
+ quality of graph drawings generated with Walshaw's algorihtm
+ |spring electrical layout'|.
+ "]],
+ examples = [["
+ \tikz \graph [spring electrical layout', coarsen=false, vertical=3 to 4]
+ {
+ { [clique] 1, 2 } -- 3 -- 4 -- { 5, 6, 7 }
+ };
+
+ \tikz \graph [spring electrical layout', coarsen, vertical=3 to 4]
+ {
+ { [clique] 1, 2 } -- 3 -- 4 -- { 5, 6, 7 }
+ };
+ "]]
+}
+
+---
+
+declare {
+ key = "minimum coarsening size",
+ type = "number",
+ initial = 2,
+
+ summary = [["
+ Defines the minimum number of nodes down to which the graph is
+ coarsened iteratively. The first graph that has a smaller or equal
+ number of nodes becomes the coarsest graph $G_l$, where $l$ is the
+ number of coarsening steps. The algorithm proceeds with the steps
+ described in the documentation of the |coarsen| option.
+ "]],
+ documentation = [["
+ In the following example the same graph is coarsened down to two
+ and four nodes, respectively. The layout of the original graph is
+ interpolated from the random initial layout and is not improved
+ further because the forces are not computed (0 iterations). Thus,
+ in the two graphs, the nodes are placed at exactly two and four
+ coordinates in the final drawing.
+ "]],
+ examples = [["
+ \tikz \graph [spring layout, iterations=0,
+ minimum coarsening size=2]
+ { subgraph C_n [n=8] };
+
+ \tikz \graph [spring layout, iterations=0,
+ minimum coarsening size=4]
+ { subgraph C_n [n=8] };
+ "]]
+}
+
+---
+
+declare {
+ key = "downsize ratio",
+ type = "number",
+ initial = "0.25",
+
+ summary = [["
+ Minimum ratio between 0 and 1 by which the number of nodes between
+ two coarse graphs $G_i$ and $G_{i+1}$ need to be reduced in order for
+ the coarsening to stop and for the algorithm to use $G_{i+1}$ as the
+ coarsest graph $G_l$. Aside from the input graph, the optimal value
+ of |downsize ratio| mostly depends on the coarsening scheme being
+ used. Possible schemes are |collapse independent edges| and
+ |connect independent nodes|.
+ "]],
+ documentation = [["
+ Increasing this option possibly reduces the number of coarse
+ graphs computed during the coarsening phase as coarsening will stop as
+ soon as a coarse graph does not reduce the number of nodes
+ substantially. This may speed up the algorithm but if the size of the
+ coarsest graph $G_l$ is much larger than |minimum coarsening size|, the
+ multilevel approach may not produce drawings as good as with a lower
+ |downsize ratio|.
+ "]],
+ examples = [["
+ % 1. ratio too high, coarsening stops early, benefits are lost
+ \tikz \graph [spring electrical layout',
+ downsize ratio=1.0,
+ node distance=7mm, vertical=3 to 4]
+ { { [clique] 1, 2 } -- 3 -- 4 -- { 5, 6, 7 } };
+
+ % 2. ratio set to default, coarsening benefits are visible
+ \tikz \graph [spring electrical layout',
+ downsize ratio=0.2,
+ node distance=7mm, vertical=3 to 4]
+ { { [clique] 1, 2 } -- 3 -- 4 -- { 5, 6, 7 } };
+ "]]
+}
+
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/force/ControlDeclare.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/force/ControlDeclare.lua
new file mode 100644
index 0000000000..af209d2c78
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/force/ControlDeclare.lua
@@ -0,0 +1,41 @@
+-- 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$
+
+
+-- Imports
+local declare = require("pgf.gd.interface.InterfaceToAlgorithms").declare
+
+
+---
+-- @section subsection {Controlling and Configuring Force-Based Algorithms}
+--
+-- All force-based algorithms are based on
+-- a general pattern which we detail in the following. Numerous options
+-- can be used to influence the behavior of this general pattern; more
+-- specific options that apply only to individual algorithms are
+-- explained along with these algorithms.
+--
+-- The vertices are initially laid out in a random configuration.
+-- Then the configuration is annealed to find a configuration of
+-- minimal energy. To avoid getting stuck in a local minimum or at a
+-- saddle point, random forces are added. All of this makes the final
+-- layout extremely susceptible to changes in the random numbers. To
+-- achieve a certain stability of the results, you should fix the
+-- random seed. However, in the recent past Lua has switched its
+-- random number generator, which means that you won't get the same
+-- sequence of random numbers as in a previous version, even for
+-- identical seed. If you rely on the long-term stability of vertex
+-- placement, you should consider using a different layout. With the
+-- spring layout you have to assume that the layout will be random.
+--
+-- @end
+
+
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/force/ControlElectric.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/force/ControlElectric.lua
new file mode 100644
index 0000000000..c9f129c28d
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/force/ControlElectric.lua
@@ -0,0 +1,105 @@
+-- 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$
+
+
+-- Imports
+local declare = require("pgf.gd.interface.InterfaceToAlgorithms").declare
+
+
+
+---
+-- @section subsubsection {Forces and Their Effects: Electrical
+-- Repulsion}
+--
+-- @end
+
+
+---
+
+declare {
+ key = "electric charge",
+ type = "number",
+ initial = 1,
+
+ summary = [["
+ Defines the electric charge of the node. The stronger the
+ |electric charge| of a node the stronger the repulsion between the
+ node and others in the graph. A negative |electric charge| means that
+ other nodes are further attracted to the node rather than repulsed,
+ although in theory this effect strongly depends on how the
+ |spring electrical layout| algorithm works.
+ Two typical effects of increasing the |electric charge| are distortion
+ of symmetries and an upscaling of the drawings.
+ "]],
+ examples = {
+ {
+ options = [["preamble={\usetikzlibrary{graphs,graphdrawing} \usegdlibrary{force}}"]],
+ code = [["
+ \tikz \graph [spring electrical layout, horizontal=0 to 1]
+ { 0 [electric charge=1] -- subgraph C_n [n=10] };
+ "]]
+ },{
+ code = [["
+ \tikz \graph [spring electrical layout, horizontal=0 to 1]
+ { 0 [electric charge=5] -- subgraph C_n [n=10] };
+ "]]
+ },{
+ code = [["
+ \tikz \graph [spring electrical layout, horizontal=0 to 1]
+ { [clique] 1 [electric charge=5], 2, 3, 4 };
+ "]]
+ }
+ }
+}
+
+
+---
+
+declare {
+ key = "electric force order",
+ type = "number",
+ initial = "1",
+
+ summary = [["
+ Sometimes, when drawing symmetric and mesh-like graphs, the
+ peripheral distortion caused by long-range electric forces may be
+ undesired. Some electric force models allow to reduce long-range
+ forces and distortion effects by increasing
+ the order (exponent) of electric forces. Values between 0 and 1
+ increase long-range electric forces and the scaling of the
+ generated layouts. Value greater than 1 decrease long-range
+ electric forces and results in shrinking drawings.
+ "]]
+ }
+
+
+---
+
+declare {
+ key = "approximate remote forces",
+ type = "boolean",
+
+ summary = [["
+ Force based algorithms often need to compute a force for each pair
+ of vertices, which, for larger numbers of vertices, can lead to a
+ significant time overhead. This problem can be addressed by
+ approximating these forces: For a vertex far removed from a cluster
+ of vertices, instead of computing the force contribution of each
+ vertex of the cluster individually, we form a sort of
+ ``supervertex'' at the ``gravitational center'' of the cluster and
+ then compute only the force between this supervertex and the single
+ vertex.
+
+ \emph{Remark:} Currently, the implementation seems to be broken, at
+ least the results are somewhat strange when this key is used.
+ "]]
+ }
+
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/force/ControlIteration.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/force/ControlIteration.lua
new file mode 100644
index 0000000000..b8d96630cd
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/force/ControlIteration.lua
@@ -0,0 +1,136 @@
+-- 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$
+
+
+-- Imports
+local declare = require("pgf.gd.interface.InterfaceToAlgorithms").declare
+
+
+
+
+---
+-- @section subsubsection {The Iterative Process and Cooling}
+--
+-- @end
+
+
+
+---
+
+declare {
+ key = "iterations",
+ type = "number",
+ initial = "500",
+
+ summary = [["
+ Limits the number of iterations of algorithms for force-based
+ layouts to \meta{number}.
+ "]],
+ documentation = [["
+ Depending on the characteristics of the input graph and the parameters
+ chosen for the algorithm, minimizing the system energy may require
+ many iterations.
+
+ In these situations it may come in handy to limit the number of
+ iterations. This feature can also be useful to draw the same graph
+ after different iterations and thereby demonstrate how the spring or
+ spring-electrical algorithm improves the drawing step by step.
+
+ The examples shows two drawings generated using two
+ different |iteration| limits.
+ "]],
+ examples = {[["
+ \tikz \graph [spring layout, iterations=10] { subgraph K_n [n=4] };
+ "]],[["
+ \tikz \graph [spring layout, iterations=500] { subgraph K_n [n=4] };
+ "]],[["
+ \tikz \graph [spring electrical layout, iterations=10]
+ { subgraph K_n [n=4] };
+ "]],[["
+ \tikz \graph [spring electrical layout, iterations=500]
+ { subgraph K_n [n=4] };
+ "]]
+ }
+}
+
+---
+
+declare {
+ key = "initial step length",
+ type = "length",
+ initial = "0",
+
+ summary = [["
+ This parameter specifies the amount by which nodes will be
+ displaced in each iteration, initially. If set to |0| (which is the
+ default), an appropriate value is computed automatically.
+ "]]
+ }
+
+---
+
+declare {
+ key = "cooling factor",
+ type = "number",
+ initial = "0.95",
+
+ summary = [["
+ This parameter helps in controlling how layouts evolve over
+ time. It is used to gradually reduce the step size
+ between one iteration to the next.
+ "]],
+ documentation = [["
+ A small positive cooling factor
+ $\ge 0$ means that the movement of nodes is quickly or abruptly
+ reduced, while a large cooling factor $\le 1$ allows for a smoother
+ step by step layout refinement at the cost of more iterations. The
+ following example demonstrates how a smaller cooling factor may
+ result in a less balanced drawing. By default, Hu2006 spring,
+ Hu2006 spring electrical, and Walshaw2000 spring electrical use a
+ cooling factor of |0.95|.
+ "]],
+ examples = {[["
+ \tikz \graph [spring layout, cooling factor=0.1]
+ { a -> b -> c -> a };
+ "]],[["
+ \tikz \graph [spring layout, cooling factor=0.5]
+ { a -> b -> c -> a };
+ "]]
+ }
+}
+
+---
+
+declare {
+ key = "convergence tolerance",
+ type = "number",
+ initial = "0.01",
+
+ summary = [["
+ All spring and spring-electrical algorithms implemented in the
+ thesis terminate as soon as the maximum movement of any node drops
+ below $k \cdot \meta{tolerance}$. This tolerance factor can be changed
+ with the convergence tolerance option:
+ "]],
+ examples = {[["
+ \tikz \graph [spring layout, convergence tolerance=0.001]
+ { { [clique] 1, 2 } -- 3 -- 4 -- { 5, 6, 7 } };
+ "]],[["
+ \tikz \graph [spring layout, convergence tolerance=1.0]
+ { { [clique] 1, 2 } -- 3 -- 4 -- { 5, 6, 7 } };
+ "]]
+ }
+}
+
+
+
+
+
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/force/ControlSprings.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/force/ControlSprings.lua
new file mode 100644
index 0000000000..9b0c1071e3
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/force/ControlSprings.lua
@@ -0,0 +1,59 @@
+-- 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$
+
+
+-- Imports
+local declare = require("pgf.gd.interface.InterfaceToAlgorithms").declare
+
+
+---
+-- @section subsubsection {Forces and Their Effects: Springs}
+--
+-- The most important parameter of springs is their ``natural
+-- length'', which can be configured using the general-purpose
+-- |node distance| parameter. It is the ``equilibrium length'' of a
+-- spring between two nodes in the graph. When an edge has this
+-- length, no forces will ``push'' or ``pull'' along the edge.
+--
+-- The following examples shows how a simple graph can be scaled by
+-- changing the |node distance|:
+-- %
+-- \begin{codeexample}[preamble={\usetikzlibrary{graphs.standard,graphdrawing}
+-- \usegdlibrary{force}}]
+-- \tikz \graph [spring layout, node distance=7mm] { subgraph C_n[n=3] };
+-- \tikz \graph [spring layout] { subgraph C_n[n=3] };
+-- \tikz \graph [spring layout, node distance=15mm]{ subgraph C_n[n=3] };
+-- \end{codeexample}
+-- %
+-- \begin{codeexample}[preamble={\usetikzlibrary{graphs.standard,graphdrawing}
+-- \usegdlibrary{force}}]
+-- \tikz \graph [spring electrical layout, node distance=0.7cm] { subgraph C_n[n=3] };
+-- \tikz \graph [spring electrical layout] { subgraph C_n[n=3] };
+-- \tikz \graph [spring electrical layout, node distance=1.5cm] { subgraph C_n[n=3] };
+-- \end{codeexample}
+--
+-- @end
+
+
+---
+
+declare {
+ key = "spring constant",
+ type = "number",
+ initial = "0.01",
+
+ summary = [["
+ The ``spring constant'' is a factor from Hooke's law describing the
+ ``stiffness'' of a spring. This factor is used inside spring-based
+ algorithms to determine how strongly edges ``pull'' and ``push'' at
+ the nodes they connect.
+ "]]
+}
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/force/ControlStart.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/force/ControlStart.lua
new file mode 100644
index 0000000000..85a64b0a5a
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/force/ControlStart.lua
@@ -0,0 +1,41 @@
+-- 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$
+
+
+-- Imports
+local declare = require("pgf.gd.interface.InterfaceToAlgorithms").declare
+
+
+---
+-- @section subsubsection {Start Configuration}
+--
+-- Currently, the start configuration for force-based algorithms is a
+-- random distribution of the vertices. You can influence it by
+-- changing the |random seed|:
+-- %
+-- \begin{codeexample}[preamble={\usetikzlibrary{graphs,graphdrawing}
+-- \usegdlibrary{force}}]
+-- \tikz \graph [random seed=10, spring layout] {
+-- a -- {b, c, d} -- e -- f -- {g,h} -- {a,b,e};
+-- };
+-- \end{codeexample}
+-- %
+-- \begin{codeexample}[preamble={\usetikzlibrary{graphs,graphdrawing}
+-- \usegdlibrary{force}}]
+-- \tikz \graph [random seed=11, spring layout] {
+-- a -- {b, c, d} -- e -- f -- {g,h} -- {a,b,e};
+-- };
+-- \end{codeexample}
+--
+-- Other methods, like a planar preembedding, are not implemented
+-- currently.
+--
+-- @end
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/force/QuadTree.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/force/QuadTree.lua
new file mode 100644
index 0000000000..3e1620dc21
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/force/QuadTree.lua
@@ -0,0 +1,280 @@
+-- Copyright 2011 by Jannis Pohlmann
+-- 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$
+
+
+--- An implementation of a quad trees.
+--
+-- The class QuadTree provides methods form handling quadtrees.
+--
+
+local QuadTree = {
+ -- Subclasses
+ Particle = {},
+ Cell = {}
+}
+QuadTree.__index = QuadTree
+
+-- Namespace:
+require("pgf.gd.force").QuadTree = QuadTree
+
+-- Imports:
+local Vector = require "pgf.gd.deprecated.Vector"
+local lib = require "pgf.gd.lib"
+
+
+--- Creates a new quad tree.
+--
+-- @return A newly-allocated quad tree.
+--
+function QuadTree.new(x, y, width, height, max_particles)
+ local tree = {
+ root_cell = QuadTree.Cell.new(x, y, width, height, max_particles)
+ }
+ setmetatable(tree, QuadTree)
+ return tree
+end
+
+
+
+--- Inserts a particle
+--
+-- @param param A particle of type QuadTree.Particle
+--
+function QuadTree:insert(particle)
+ self.root_cell:insert(particle)
+end
+
+
+
+--- Computes the interactions of a particle with other cells
+--
+-- @param particle A particle
+-- @param test_func A test function, which on input of a cubical cell and a particle should
+-- decide whether the cubical cell should be inserted into the result
+-- @param cells An optional array of cells, to which the found cells will be added
+--
+-- @return The cells array or a new array, if it was empty.
+--
+function QuadTree:findInteractionCells(particle, test_func, cells)
+ local test_func = test_func or function (cell, particle) return true end
+ cells = cells or {}
+
+ self.root_cell:findInteractionCells(particle, test_func, cells)
+
+ return cells
+end
+
+
+
+
+--- Particle subclass
+QuadTree.Particle.__index = QuadTree.Particle
+
+
+
+--- Creates a new particle.
+--
+-- @return A newly-allocated particle.
+--
+function QuadTree.Particle.new(pos, mass)
+ local particle = {
+ pos = pos:copy(),
+ mass = mass or 1,
+ subparticles = {},
+ }
+ setmetatable(particle, QuadTree.Particle)
+ return particle
+end
+
+
+
+--- A cell of a quadtree
+--
+-- TT: Why is it called "cubical", by the way?!
+
+QuadTree.Cell.__index = QuadTree.Cell
+
+
+
+--- Creates a new cubicle cell.
+--
+-- @return a newly-allocated cubicle cell.
+--
+function QuadTree.Cell.new(x, y, width, height, max_particles)
+ local cell = {
+ x = x,
+ y = y,
+ width = width,
+ height = height,
+ max_particles = max_particles or 1,
+ subcells = {},
+ particles = {},
+ center_of_mass = nil,
+ mass = 0,
+ }
+ setmetatable(cell, QuadTree.Cell)
+ return cell
+end
+
+
+
+function QuadTree.Cell:containsParticle(particle)
+ return particle.pos.x >= self.x and particle.pos.x <= self.x + self.width
+ and particle.pos.y >= self.y and particle.pos.y <= self.y + self.height
+end
+
+
+
+function QuadTree.Cell:findSubcell(particle)
+ return lib.find(self.subcells, function (cell)
+ return cell:containsParticle(particle)
+ end)
+end
+
+
+
+function QuadTree.Cell:createSubcells()
+ assert(type(self.subcells) == 'table' and #self.subcells == 0)
+ assert(type(self.particles) == 'table' and #self.particles <= self.max_particles)
+
+ if #self.subcells == 0 then
+ for _,x in ipairs({self.x, self.x + self.width/2}) do
+ for _,y in ipairs({self.y, self.y + self.height/2}) do
+ local cell = QuadTree.Cell.new(x, y, self.width/2, self.height/2, self.max_particles)
+ table.insert(self.subcells, cell)
+ end
+ end
+ end
+end
+
+
+
+function QuadTree.Cell:insert(particle)
+ -- check if we have a particle with the exact same position already
+ local existing = lib.find(self.particles, function (other)
+ return other.pos:equals(particle.pos)
+ end)
+
+ if existing then
+ -- we already have a particle at the same position; splitting the cell
+ -- up makes no sense; instead we add the new particle as a
+ -- subparticle of the existing one
+ table.insert(existing.subparticles, particle)
+ else
+ if #self.subcells == 0 and #self.particles < self.max_particles then
+ table.insert(self.particles, particle)
+ else
+ if #self.subcells == 0 then
+ self:createSubcells()
+ end
+
+ -- move particles to the new subcells
+ for _,existing in ipairs(self.particles) do
+ local cell = self:findSubcell(existing)
+ assert(cell, 'failed to find a cell for particle ' .. tostring(existing.pos))
+ cell:insert(existing)
+ end
+
+ self.particles = {}
+
+ local cell = self:findSubcell(particle)
+ assert(cell)
+ cell:insert(particle)
+ end
+ end
+
+ self:updateMass()
+ self:updateCenterOfMass()
+
+ assert(self.mass)
+ assert(self.center_of_mass)
+end
+
+
+
+function QuadTree.Cell:updateMass()
+ -- reset mass to zero
+ self.mass = 0
+
+ if #self.subcells == 0 then
+ -- the mass is the number of particles of the cell
+ for _,particle in ipairs(self.particles) do
+ self.mass = self.mass + particle.mass
+ for _,subparticle in ipairs(particle.subparticles) do
+ self.mass = self.mass + subparticle.mass
+ end
+ end
+ else
+ -- the mass is the sum of the masses of the subcells
+ for _,subcell in ipairs(self.subcells) do
+ self.mass = self.mass + subcell.mass
+ end
+ end
+end
+
+
+
+function QuadTree.Cell:updateCenterOfMass()
+ -- reset center of mass, assuming the cell is empty
+ self.center_of_mass = nil
+
+ if #self.subcells == 0 then
+ -- the center of mass is the average position of the particles
+ -- weighted by their masses
+ self.center_of_mass = Vector.new (2)
+ for _,p in ipairs(self.particles) do
+ for _,sp in ipairs(p.subparticles) do
+ self.center_of_mass = self.center_of_mass:plus(sp.pos:timesScalar(sp.mass))
+ end
+ self.center_of_mass = self.center_of_mass:plus(p.pos:timesScalar(p.mass))
+ end
+ self.center_of_mass = self.center_of_mass:dividedByScalar(self.mass)
+ else
+ -- the center of mass is the average of the weighted centers of mass
+ -- of the subcells
+ self.center_of_mass = Vector.new(2)
+ for _,sc in ipairs(self.subcells) do
+ if sc.center_of_mass then
+ self.center_of_mass = self.center_of_mass:plus(sc.center_of_mass:timesScalar(sc.mass))
+ else
+ assert(sc.mass == 0)
+ end
+ end
+ self.center_of_mass = self.center_of_mass:dividedByScalar(self.mass)
+ end
+end
+
+
+
+function QuadTree.Cell:findInteractionCells(particle, test_func, cells)
+ if #self.subcells == 0 or test_func(self, particle) then
+ table.insert(cells, self)
+ else
+ for _,subcell in ipairs(self.subcells) do
+ subcell:findInteractionCells(particle, test_func, cells)
+ end
+ end
+end
+
+
+function QuadTree.Cell:__tostring()
+ return '((' .. self.x .. ', ' .. self.y .. ') '
+ .. 'to (' .. self.x + self.width .. ', ' .. self.y + self.height .. '))'
+ .. (self.particle and ' => ' .. self.particle.name or '')
+ .. (self.center_of_mass and ' mass ' .. self.mass .. ' at ' .. tostring(self.center_of_mass) or '')
+end
+
+
+
+-- done
+
+return QuadTree
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/force/SpringElectricalHu2006.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/force/SpringElectricalHu2006.lua
new file mode 100644
index 0000000000..a7230eb1f6
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/force/SpringElectricalHu2006.lua
@@ -0,0 +1,633 @@
+-- Copyright 2011 by Jannis Pohlmann
+--
+-- 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
+
+-- @release $Header$
+
+
+local SpringElectricalHu2006 = {}
+
+-- Imports
+local declare = require("pgf.gd.interface.InterfaceToAlgorithms").declare
+
+
+
+
+---
+
+declare {
+ key = "spring electrical Hu 2006 layout",
+ algorithm = SpringElectricalHu2006,
+
+ preconditions = {
+ connected = true,
+ loop_free = true,
+ simple = true,
+ },
+
+ old_graph_model = true,
+
+ summary = [["
+ Implementation of a spring electrical graph drawing algorithm based on
+ a paper by Hu.
+ "]],
+ documentation = [["
+ \begin{itemize}
+ \item
+ Y. Hu.
+ \newblock Efficient, high-quality force-directed graph drawing.
+ \newblock \emph{The Mathematica Journal}, 2006.
+ \end{itemize}
+
+ There are some modifications compared to the original algorithm,
+ see the Diploma thesis of Pohlmann for details.
+ "]]
+}
+
+-- Imports
+
+local PathLengths = require "pgf.gd.lib.PathLengths"
+local Vector = require "pgf.gd.deprecated.Vector"
+
+local QuadTree = require "pgf.gd.force.QuadTree"
+local CoarseGraph = require "pgf.gd.force.CoarseGraph"
+
+local lib = require "pgf.gd.lib"
+
+
+function SpringElectricalHu2006:run()
+
+ -- Setup properties
+ local options = self.digraph.options
+
+ self.iterations = options['iterations']
+ self.cooling_factor = options['cooling factor']
+ self.initial_step_length = options['initial step length']
+ self.convergence_tolerance = options['convergence tolerance']
+
+ self.natural_spring_length = options['node distance']
+ self.spring_constant = options['spring constant']
+
+ self.approximate_repulsive_forces = options['approximate remote forces']
+ self.repulsive_force_order = options['electric force order']
+
+ self.coarsen = options['coarsen']
+ self.downsize_ratio = options['downsize ratio']
+ self.minimum_graph_size = options['minimum coarsening size']
+
+ -- Adjust types
+ self.downsize_ratio = math.max(0, math.min(1, self.downsize_ratio))
+ self.graph_size = #self.graph.nodes
+ self.graph_density = (2 * #self.graph.edges) / (#self.graph.nodes * (#self.graph.nodes - 1))
+
+ -- validate input parameters
+ assert(self.iterations >= 0, 'iterations (value: ' .. self.iterations .. ') need to be greater than 0')
+ assert(self.cooling_factor >= 0 and self.cooling_factor <= 1, 'the cooling factor (value: ' .. self.cooling_factor .. ') needs to be between 0 and 1')
+ assert(self.initial_step_length >= 0, 'the initial step length (value: ' .. self.initial_step_length .. ') needs to be greater than or equal to 0')
+ assert(self.convergence_tolerance >= 0, 'the convergence tolerance (value: ' .. self.convergence_tolerance .. ') needs to be greater than or equal to 0')
+ assert(self.natural_spring_length >= 0, 'the natural spring dimension (value: ' .. self.natural_spring_length .. ') needs to be greater than or equal to 0')
+ assert(self.spring_constant >= 0, 'the spring constant (value: ' .. self.spring_constant .. ') needs to be greater or equal to 0')
+ assert(self.downsize_ratio >= 0 and self.downsize_ratio <= 1, 'the downsize ratio (value: ' .. self.downsize_ratio .. ') needs to be between 0 and 1')
+ assert(self.minimum_graph_size >= 2, 'the minimum coarsening size of coarse graphs (value: ' .. self.minimum_graph_size .. ') needs to be greater than or equal to 2')
+
+ -- initialize node weights
+ for _,node in ipairs(self.graph.nodes) do
+ if node:getOption('electric charge') ~= nil then
+ node.weight = node:getOption('electric charge')
+ else
+ node.weight = 1
+ end
+ end
+
+ -- initialize edge weights
+ for _,edge in ipairs(self.graph.edges) do
+ edge.weight = 1
+ end
+
+ -- initialize the coarse graph data structure. note that the algorithm
+ -- is the same regardless whether coarsening is used, except that the
+ -- number of coarsening steps without coarsening is 0
+ local coarse_graph = CoarseGraph.new(self.graph)
+
+ -- check if the multilevel approach should be used
+ if self.coarsen 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
+ while coarse_graph:getSize() > self.minimum_graph_size
+ and coarse_graph:getRatio() <= (1 - self.downsize_ratio)
+ do
+ coarse_graph:coarsen()
+ end
+ end
+
+ if self.coarsen then
+ -- use the natural spring length as the initial natural spring length
+ local spring_length = self.natural_spring_length
+
+ -- compute a random initial layout for the coarsest graph
+ self:computeInitialLayout(coarse_graph.graph, spring_length)
+
+ -- set the spring length to the average edge length of the initial layout
+ spring_length = 0
+ for _,edge in ipairs(coarse_graph.graph.edges) do
+ spring_length = spring_length + edge.nodes[1].pos:minus(edge.nodes[2].pos):norm()
+ end
+ spring_length = spring_length / #coarse_graph.graph.edges
+
+ -- 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 then
+ self:computeForceLayout(coarse_graph.graph, spring_length, SpringElectricalHu2006.adaptive_step_update)
+ end
+
+ -- undo coarsening step by step, applying the force-based sub-algorithm
+ -- to every intermediate coarse graph as well as the original graph
+ while coarse_graph:getLevel() > 0 do
+
+ -- compute the diameter of the parent coarse graph
+ local parent_diameter = PathLengths.pseudoDiameter(coarse_graph.graph)
+
+ -- interpolate the previous coarse graph from its parent
+ coarse_graph:interpolate()
+
+ -- compute the diameter of the current coarse graph
+ local current_diameter = PathLengths.pseudoDiameter(coarse_graph.graph)
+
+ -- scale node positions by the quotient of the pseudo diameters
+ for _,node in ipairs(coarse_graph.graph) do
+ node.pos:update(function (n, value)
+ return value * (current_diameter / parent_diameter)
+ end)
+ end
+
+ -- compute forces in the graph
+ self:computeForceLayout(coarse_graph.graph, spring_length, SpringElectricalHu2006.conservative_step_update)
+ end
+ else
+ -- compute a random initial layout for the coarsest graph
+ self:computeInitialLayout(coarse_graph.graph, self.natural_spring_length)
+
+ -- set the spring length to the average edge length of the initial layout
+ spring_length = 0
+ for _,edge in ipairs(coarse_graph.graph.edges) do
+ spring_length = spring_length + edge.nodes[1].pos:minus(edge.nodes[2].pos):norm()
+ end
+ spring_length = spring_length / #coarse_graph.graph.edges
+
+ -- improve the layout with the force-based algorithm
+ self:computeForceLayout(coarse_graph.graph, spring_length, SpringElectricalHu2006.adaptive_step_update)
+ end
+end
+
+
+
+function SpringElectricalHu2006:computeInitialLayout(graph, spring_length)
+ -- TODO how can supernodes and fixed nodes go hand in hand?
+ -- maybe fix the supernode if at least one of its subnodes is
+ -- fixated?
+
+ -- fixate all nodes that have a 'desired at' option. this will set the
+ -- node.fixed member to true and also set node.pos.x and node.pos.y
+ self:fixateNodes(graph)
+
+ if #graph.nodes == 2 then
+ if not (graph.nodes[1].fixed and graph.nodes[2].fixed) then
+ local fixed_index = graph.nodes[2].fixed and 2 or 1
+ local loose_index = graph.nodes[2].fixed and 1 or 2
+
+ if not graph.nodes[1].fixed and not graph.nodes[2].fixed then
+ -- both nodes can be moved, so we assume node 1 is fixed at (0,0)
+ graph.nodes[1].pos.x = 0
+ graph.nodes[1].pos.y = 0
+ end
+
+ -- position the loose node relative to the fixed node, with
+ -- the displacement (random direction) matching the spring length
+ local direction = Vector.new{x = lib.random(1, spring_length), y = lib.random(1, spring_length)}
+ local distance = 3 * spring_length * self.graph_density * math.sqrt(self.graph_size) / 2
+ local displacement = direction:normalized():timesScalar(distance)
+
+ graph.nodes[loose_index].pos = graph.nodes[fixed_index].pos:plus(displacement)
+ else
+ -- both nodes are fixed, initial layout may be far from optimal
+ end
+ else
+
+ -- use a random positioning technique
+ local function positioning_func(n)
+ local radius = 3 * spring_length * self.graph_density * math.sqrt(self.graph_size) / 2
+ return lib.random(-radius, radius)
+ end
+
+ -- compute initial layout based on the random positioning technique
+ for _,node in ipairs(graph.nodes) do
+ if not node.fixed then
+ node.pos.x = positioning_func(1)
+ node.pos.y = positioning_func(2)
+ end
+ end
+ end
+end
+
+
+
+function SpringElectricalHu2006:computeForceLayout(graph, spring_length, step_update_func)
+ -- global (=repulsive) force function
+ function accurate_repulsive_force(distance, weight)
+ -- note: the weight is taken into the equation here. unlike in the original
+ -- algorithm different electric charges are allowed for each node in this
+ -- implementation
+ return - weight * self.spring_constant * math.pow(spring_length, self.repulsive_force_order + 1) / math.pow(distance, self.repulsive_force_order)
+ end
+
+ -- global (=repulsive, approximated) force function
+ function approximated_repulsive_force(distance, mass)
+ return - mass * self.spring_constant * math.pow(spring_length, self.repulsive_force_order + 1) / math.pow(distance, self.repulsive_force_order)
+ end
+
+ -- local (spring) force function
+ function attractive_force(distance)
+ return (distance * distance) / spring_length
+ end
+
+ -- define the Barnes-Hut opening criterion
+ function barnes_hut_criterion(cell, particle)
+ local distance = particle.pos:minus(cell.center_of_mass):norm()
+ return cell.width / distance <= 1.2
+ end
+
+ -- fixate all nodes that have a 'desired at' option. this will set the
+ -- node.fixed member to true and also set node.pos.x and node.pos.y
+ self:fixateNodes(graph)
+
+ -- adjust the initial step length automatically if desired by the user
+ local step_length = self.initial_step_length == 0 and spring_length or self.initial_step_length
+
+ -- convergence criteria etc.
+ local converged = false
+ local energy = math.huge
+ local iteration = 0
+ local progress = 0
+
+ while not converged and iteration < self.iterations do
+ -- remember old node positions
+ local old_positions = lib.map(graph.nodes, function (node)
+ return node.pos:copy(), node
+ end)
+
+ -- remember the old system energy and reset it for the current iteration
+ local old_energy = energy
+ energy = 0
+
+ -- build the quadtree for approximating repulsive forces, if desired
+ local quadtree = nil
+ if self.approximate_repulsive_forces then
+ quadtree = self:buildQuadtree(graph)
+ end
+
+ for _,v in ipairs(graph.nodes) do
+ if not v.fixed then
+ -- vector for the displacement of v
+ local d = Vector.new(2)
+
+ -- compute repulsive forces
+ if self.approximate_repulsive_forces then
+ -- determine the cells that have a repulsive influence on v
+ local cells = quadtree:findInteractionCells(v, barnes_hut_criterion)
+
+ -- compute the repulsive force between these cells and v
+ for _,cell in ipairs(cells) do
+ -- check if the cell is a leaf
+ if #cell.subcells == 0 then
+ -- compute the forces between the node and all particles in the cell
+ for _,particle in ipairs(cell.particles) do
+ local real_particles = lib.copy(particle.subparticles)
+ table.insert(real_particles, particle)
+
+ for _,real_particle in ipairs(real_particles) do
+ local delta = real_particle.pos:minus(v.pos)
+
+ -- enforce a small virtual distance if the node and the cell's
+ -- center of mass are located at (almost) the same position
+ if delta:norm() < 0.1 then
+ delta:update(function (n, value) return 0.1 + lib.random() * 0.1 end)
+ end
+
+ -- compute the repulsive force vector
+ local repulsive_force = approximated_repulsive_force(delta:norm(), real_particle.mass)
+ local force = delta:normalized():timesScalar(repulsive_force)
+
+ -- move the node v accordingly
+ d = d:plus(force)
+ end
+ end
+ else
+ -- compute the distance between the node and the cell's center of mass
+ local delta = cell.center_of_mass:minus(v.pos)
+
+ -- enforce a small virtual distance if the node and the cell's
+ -- center of mass are located at (almost) the same position
+ if delta:norm() < 0.1 then
+ delta:update(function (n, value) return 0.1 + lib.random() * 0.1 end)
+ end
+
+ -- compute the repulsive force vector
+ local repulsive_force = approximated_repulsive_force(delta:norm(), cell.mass)
+ local force = delta:normalized():timesScalar(repulsive_force)
+
+ -- move the node v accordingly
+ d = d:plus(force)
+ end
+ end
+ else
+ for _,u in ipairs(graph.nodes) do
+ if v ~= u then
+ -- compute the distance between u and v
+ local delta = u.pos:minus(v.pos)
+
+ -- enforce a small virtual distance if the nodes are
+ -- located at (almost) the same position
+ if delta:norm() < 0.1 then
+ delta:update(function (n, value) return 0.1 + lib.random() * 0.1 end)
+ end
+
+ -- compute the repulsive force vector
+ local repulsive_force = accurate_repulsive_force(delta:norm(), u.weight)
+ local force = delta:normalized():timesScalar(repulsive_force)
+
+ -- move the node v accordingly
+ d = d:plus(force)
+ end
+ end
+ end
+
+ -- compute attractive forces between v and its neighbors
+ for _,edge in ipairs(v.edges) do
+ local u = edge:getNeighbour(v)
+
+ -- compute the distance between u and v
+ local delta = u.pos:minus(v.pos)
+
+ -- enforce a small virtual distance if the nodes are
+ -- located at (almost) the same position
+ if delta:norm() < 0.1 then
+ delta:update(function (n, value) return 0.1 + lib.random() * 0.1 end)
+ end
+
+ -- compute the spring force vector between u and v
+ local attr_force = attractive_force(delta:norm())
+ local force = delta:normalized():timesScalar(attr_force)
+
+ -- move the node v accordingly
+ d = d:plus(force)
+ end
+
+ -- really move the node now
+ -- TODO note how all nodes are moved by the same amount (step_length)
+ -- while Walshaw multiplies the normalized force with min(step_length,
+ -- d:norm()). could that improve this algorithm even further?
+ v.pos = v.pos:plus(d:normalized():timesScalar(step_length))
+
+ -- TODO Hu doesn't mention this but the energy of a particle is
+ -- typically considered as the product of its mass and the square of
+ -- its forces. This means we should probably take the weight of
+ -- the node v into the equation, doesn't it?
+ --
+ -- update the energy function
+ energy = energy + math.pow(d:norm(), 2)
+ -- vector for the displacement of v
+ local d = Vector.new(2)
+
+ -- compute repulsive forces
+ if self.approximate_repulsive_forces then
+ -- determine the cells that have a repulsive influence on v
+ local cells = quadtree:findInteractionCells(v, barnes_hut_criterion)
+
+ -- compute the repulsive force between these cells and v
+ for _,cell in ipairs(cells) do
+ -- check if the cell is a leaf
+ if #cell.subcells == 0 then
+ -- compute the forces between the node and all particles in the cell
+ for _,particle in ipairs(cell.particles) do
+ local real_particles = lib.copy(particle.subparticles)
+ table.insert(real_particles, particle)
+
+ for _,real_particle in ipairs(real_particles) do
+ local delta = real_particle.pos:minus(v.pos)
+
+ -- enforce a small virtual distance if the node and the cell's
+ -- center of mass are located at (almost) the same position
+ if delta:norm() < 0.1 then
+ delta:update(function (n, value) return 0.1 + lib.random() * 0.1 end)
+ end
+
+ -- compute the repulsive force vector
+ local repulsive_force = approximated_repulsive_force(delta:norm(), real_particle.mass)
+ local force = delta:normalized():timesScalar(repulsive_force)
+
+ -- move the node v accordingly
+ d = d:plus(force)
+ end
+ end
+ else
+ -- compute the distance between the node and the cell's center of mass
+ local delta = cell.center_of_mass:minus(v.pos)
+
+ -- enforce a small virtual distance if the node and the cell's
+ -- center of mass are located at (almost) the same position
+ if delta:norm() < 0.1 then
+ delta:update(function (n, value) return 0.1 + lib.random() * 0.1 end)
+ end
+
+ -- compute the repulsive force vector
+ local repulsive_force = approximated_repulsive_force(delta:norm(), cell.mass)
+ local force = delta:normalized():timesScalar(repulsive_force)
+
+ -- move the node v accordingly
+ d = d:plus(force)
+ end
+ end
+ else
+ for _,u in ipairs(graph.nodes) do
+ if v ~= u then
+ -- compute the distance between u and v
+ local delta = u.pos:minus(v.pos)
+
+ -- enforce a small virtual distance if the nodes are
+ -- located at (almost) the same position
+ if delta:norm() < 0.1 then
+ delta:update(function (n, value) return 0.1 + lib.random() * 0.1 end)
+ end
+
+ -- compute the repulsive force vector
+ local repulsive_force = accurate_repulsive_force(delta:norm(), u.weight)
+ local force = delta:normalized():timesScalar(repulsive_force)
+
+ -- move the node v accordingly
+ d = d:plus(force)
+ end
+ end
+ end
+
+ -- compute attractive forces between v and its neighbours
+ for _,edge in ipairs(v.edges) do
+ local u = edge:getNeighbour(v)
+
+ -- compute the distance between u and v
+ local delta = u.pos:minus(v.pos)
+
+ -- enforce a small virtual distance if the nodes are
+ -- located at (almost) the same position
+ if delta:norm() < 0.1 then
+ delta:update(function (n, value) return 0.1 + lib.random() * 0.1 end)
+ end
+
+ -- compute the spring force vector between u and v
+ local attr_force = attractive_force(delta:norm())
+ local force = delta:normalized():timesScalar(attr_force)
+
+ -- move the node v accordingly
+ d = d:plus(force)
+ end
+
+ -- really move the node now
+ -- TODO note how all nodes are moved by the same amount (step_length)
+ -- while Walshaw multiplies the normalized force with min(step_length,
+ -- d:norm()). could that improve this algorithm even further?
+ v.pos = v.pos:plus(d:normalized():timesScalar(step_length))
+
+ -- TODO Hu doesn't mention this but the energy of a particle is
+ -- typically considered as the product of its mass and the square of
+ -- its forces. This means we should probably take the weight of
+ -- the node v into the equation, doesn't it?
+ --
+ -- update the energy function
+ energy = energy + math.pow(d:norm(), 2)
+ end
+ end
+
+ -- update the step length and progress counter
+ step_length, progress = step_update_func(step_length, self.cooling_factor, energy, old_energy, progress)
+
+ -- compute the maximum node movement in this iteration
+ local max_movement = 0
+ for _,x in ipairs(graph.nodes) do
+ local delta = x.pos:minus(old_positions[x])
+ max_movement = math.max(delta:norm(), max_movement)
+ end
+
+ -- the algorithm will converge if the maximum movement is below a
+ -- threshold depending on the spring length and the convergence
+ -- tolerance
+ if max_movement < spring_length * self.convergence_tolerance then
+ converged = true
+ end
+
+ -- increment the iteration counter
+ iteration = iteration + 1
+ end
+end
+
+
+
+-- Fixes nodes at their specified positions.
+--
+function SpringElectricalHu2006:fixateNodes(graph)
+ local number_of_fixed_nodes = 0
+
+ for _,node in ipairs(graph.nodes) do
+ -- read the 'desired at' option of the node
+ local coordinate = node:getOption('desired at')
+
+ if coordinate then
+ -- apply the coordinate
+ node.pos.x = coordinate.x
+ node.pos.y = coordinate.y
+
+ -- mark the node as fixed
+ node.fixed = true
+
+ number_of_fixed_nodes = number_of_fixed_nodes + 1
+ end
+ end
+ if number_of_fixed_nodes > 1 then
+ self.growth_direction = "fixed" -- do not grow, orientation is now fixed
+ end
+end
+
+
+
+function SpringElectricalHu2006:buildQuadtree(graph)
+ -- compute the minimum x and y coordinates of all nodes
+ local min_pos = graph.nodes[1].pos
+ for _,node in ipairs(graph.nodes) do
+ min_pos = Vector.new(2, function (n) return math.min(min_pos[n], node.pos[n]) end)
+ end
+
+ -- compute maximum x and y coordinates of all nodes
+ local max_pos = graph.nodes[1].pos
+ for _,node in ipairs(graph.nodes) do
+ max_pos = Vector.new(2, function (n) return math.max(max_pos[n], node.pos[n]) end)
+ end
+
+ -- make sure the maximum position is at least a tiny bit
+ -- larger than the minimum position
+ if min_pos:equals(max_pos) then
+ max_pos = max_pos:plus(Vector.new(2, function (n)
+ return 0.1 + lib.random() * 0.1
+ end))
+ end
+
+ -- make sure to make the quadtree area slightly larger than required
+ -- in theory; for some reason Lua will otherwise think that nodes with
+ -- min/max x/y coordinates are outside the box... weird? yes.
+ min_pos = min_pos:minus({1,1})
+ max_pos = max_pos:plus({1,1})
+
+ -- create the quadtree
+ quadtree = QuadTree.new(min_pos.x, min_pos.y,
+ max_pos.x - min_pos.x,
+ max_pos.y - min_pos.y)
+
+ -- insert nodes into the quadtree
+ for _,node in ipairs(graph.nodes) do
+ local particle = QuadTree.Particle.new(node.pos, node.weight)
+ particle.node = node
+ quadtree:insert(particle)
+ end
+
+ return quadtree
+end
+
+
+
+function SpringElectricalHu2006.conservative_step_update(step, cooling_factor)
+ return cooling_factor * step, nil
+end
+
+
+
+function SpringElectricalHu2006.adaptive_step_update(step, cooling_factor, energy, old_energy, progress)
+ if energy < old_energy then
+ progress = progress + 1
+ if progress >= 5 then
+ progress = 0
+ step = step / cooling_factor
+ end
+ else
+ progress = 0
+ step = cooling_factor * step
+ end
+ return step, progress
+end
+
+
+-- done
+
+return SpringElectricalHu2006
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/force/SpringElectricalLayouts.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/force/SpringElectricalLayouts.lua
new file mode 100644
index 0000000000..6ab74fc367
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/force/SpringElectricalLayouts.lua
@@ -0,0 +1,54 @@
+-- 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$
+
+
+-- Imports
+local declare = require("pgf.gd.interface.InterfaceToAlgorithms").declare
+
+
+---
+-- @section subsection {Spring Electrical Layouts}
+--
+-- @end
+
+
+
+---
+
+declare {
+ key = "spring electrical layout",
+ use = {
+ { key = "spring electrical Hu 2006 layout" },
+ { key = "spring constant", value = "0.2" }
+ },
+
+ summary = [["
+ This key selects Hu's 2006 spring electrical layout with
+ appropriate settings for some parameters.
+ "]]
+}
+
+
+---
+
+declare {
+ key = "spring electrical layout'",
+ use = {
+ { key = "spring electrical Walshaw 2000 layout" },
+ { key = "spring constant", value = "0.01" },
+ { key = "convergence tolerance", value = "0.001" },
+ },
+
+ summary = [["
+ This key selects Walshaw's 2000 spring electrical layout with
+ appropriate settings for some parameters.
+ "]]
+}
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/force/SpringElectricalWalshaw2000.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/force/SpringElectricalWalshaw2000.lua
new file mode 100644
index 0000000000..5f7978da4e
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/force/SpringElectricalWalshaw2000.lua
@@ -0,0 +1,520 @@
+-- Copyright 2011 by Jannis Pohlmann
+-- Copyright 2012 by 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
+
+-- @release $Header$
+
+
+
+
+local SpringElectricalWalshaw2000 = {}
+
+-- Imports
+local declare = require("pgf.gd.interface.InterfaceToAlgorithms").declare
+
+
+
+
+---
+
+declare {
+ key = "spring electrical Walshaw 2000 layout",
+ algorithm = SpringElectricalWalshaw2000,
+
+ preconditions = {
+ connected = true,
+ loop_free = true,
+ simple = true,
+ },
+
+ old_graph_model = true,
+
+ summary = [["
+ Implementation of a spring electrical graph drawing algorithm based on
+ a paper by Walshaw.
+ "]],
+ documentation = [["
+ \begin{itemize}
+ \item
+ C. Walshaw.
+ \newblock A multilevel algorithm for force-directed graph drawing.
+ \newblock In J. Marks, editor, \emph{Graph Drawing}, Lecture Notes in
+ Computer Science, 1984:31--55, 2001.
+ \end{itemize}
+
+ The following modifications compared to the original algorithm were applied:
+ %
+ \begin{itemize}
+ \item An iteration limit was added.
+ \item The natural spring length for all coarse graphs is computed based
+ on the formula presented by Walshaw, so that the natural spring
+ length of the original graph (coarse graph 0) is the same as
+ the value requested by the user.
+ \item Users can define custom node and edge weights.
+ \item Coarsening stops when $|V(G_i+1)|/|V(G_i)| < p$ where $p = 0.75$.
+ \item Coarsening stops when the maximal matching is empty.
+ \item The runtime of the algorithm is improved by use of a quadtree
+ data structure like Hu does in his algorithm.
+ \item A limiting the number of levels of the quadtree is not implemented.
+ \end{itemize}
+ "]]
+}
+
+-- TODO Implement the following keys (or whatever seems appropriate
+-- and doable for this algorithm):
+-- - /tikz/desired at
+-- - /tikz/influence cutoff distance
+-- - /tikz/spring stiffness (could this be the equivalent to the electric
+-- charge of nodes?
+-- - /tikz/natural spring dimension per edge
+--
+-- TODO Implement the following features:
+-- - clustering of nodes using color classes
+-- - different cluster layouts (vertical line, horizontal line,
+-- normal cluster, internally fixed subgraph)
+
+
+
+local Vector = require "pgf.gd.deprecated.Vector"
+
+local QuadTree = require "pgf.gd.force.QuadTree"
+local CoarseGraph = require "pgf.gd.force.CoarseGraph"
+
+
+local lib = require "pgf.gd.lib"
+
+
+function SpringElectricalWalshaw2000:run()
+
+ -- Setup parameters
+ local options = self.digraph.options
+
+ self.iterations = options['iterations']
+ self.cooling_factor = options['cooling factor']
+ self.initial_step_length = options['initial step length']
+ self.convergence_tolerance = options['convergence tolerance']
+
+ self.natural_spring_length = options['node distance']
+ self.spring_constant = options['spring constant']
+
+ self.approximate_repulsive_forces = options['approximate remote forces']
+ self.repulsive_force_order = options['electric force order']
+
+ self.coarsen = options['coarsen']
+ self.downsize_ratio = options['downsize ratio']
+ self.minimum_graph_size = options['minimum coarsening size']
+
+ -- Adjust types
+ self.downsize_ratio = math.max(0, math.min(1, self.downsize_ratio))
+ self.graph_size = #self.graph.nodes
+ self.graph_density = (2 * #self.graph.edges) / (#self.graph.nodes * (#self.graph.nodes - 1))
+
+ -- validate input parameters
+ assert(self.iterations >= 0, 'iterations (value: ' .. self.iterations .. ') need to be greater than 0')
+ assert(self.cooling_factor >= 0 and self.cooling_factor <= 1, 'the cooling factor (value: ' .. self.cooling_factor .. ') needs to be between 0 and 1')
+ assert(self.initial_step_length >= 0, 'the initial step length (value: ' .. self.initial_step_length .. ') needs to be greater than or equal to 0')
+ assert(self.convergence_tolerance >= 0, 'the convergence tolerance (value: ' .. self.convergence_tolerance .. ') needs to be greater than or equal to 0')
+ assert(self.natural_spring_length >= 0, 'the natural spring dimension (value: ' .. self.natural_spring_length .. ') needs to be greater than or equal to 0')
+ assert(self.spring_constant >= 0, 'the spring constant (value: ' .. self.spring_constant .. ') needs to be greater or equal to 0')
+ assert(self.downsize_ratio >= 0 and self.downsize_ratio <= 1, 'the downsize ratio (value: ' .. self.downsize_ratio .. ') needs to be between 0 and 1')
+ assert(self.minimum_graph_size >= 2, 'the minimum coarsening size of coarse graphs (value: ' .. self.minimum_graph_size .. ') needs to be greater than or equal to 2')
+
+ -- initialize node weights
+ for _,node in ipairs(self.graph.nodes) do
+ if node:getOption('electric charge') ~= nil then
+ node.weight = node:getOption('electric charge')
+ else
+ node.weight = 1
+ end
+
+ -- a node is charged if its weight derives from the default setting
+ -- of 1 (where it has no influence on the forces)
+ node.charged = node.weight ~= 1
+ end
+
+ -- initialize edge weights
+ for _,edge in ipairs(self.graph.edges) do
+ edge.weight = 1
+ end
+
+
+ -- initialize the coarse graph data structure. note that the algorithm
+ -- is the same regardless whether coarsening is used, except that the
+ -- number of coarsening steps without coarsening is 0
+ local coarse_graph = CoarseGraph.new(self.graph)
+
+ -- check if the multilevel approach should be used
+ if self.coarsen 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
+ while coarse_graph:getSize() > self.minimum_graph_size
+ and coarse_graph:getRatio() < (1 - self.downsize_ratio)
+ do
+ coarse_graph:coarsen()
+ end
+ end
+
+ -- compute the natural spring length for the coarsest graph in a way
+ -- that will result in the desired natural spring length in the
+ -- original graph
+ local spring_length = self.natural_spring_length / math.pow(math.sqrt(4/7), coarse_graph:getLevel())
+
+ if self.coarsen then
+ -- generate a random initial layout for the coarsest graph
+ self:computeInitialLayout(coarse_graph.graph, spring_length)
+
+ -- undo coarsening step by step, applying the force-based sub-algorithm
+ -- to every intermediate coarse graph as well as the original graph
+ while coarse_graph:getLevel() > 0 do
+ -- interpolate the previous coarse graph
+ coarse_graph:interpolate()
+
+ -- update the natural spring length so that, for the original graph,
+ -- it equals the natural spring dimension configured by the user
+ spring_length = spring_length * math.sqrt(4/7)
+
+ -- apply the force-based algorithm to improve the layout
+ self:computeForceLayout(coarse_graph.graph, spring_length)
+ end
+ else
+ -- generate a random initial layout for the coarsest graph
+ self:computeInitialLayout(coarse_graph.graph, spring_length)
+
+ -- apply the force-based algorithm to improve the layout
+ self:computeForceLayout(coarse_graph.graph, spring_length)
+ end
+end
+
+
+
+function SpringElectricalWalshaw2000:computeInitialLayout(graph, spring_length)
+ -- TODO how can supernodes and fixed nodes go hand in hand?
+ -- maybe fix the supernode if at least one of its subnodes is
+ -- fixated?
+
+ -- fixate all nodes that have a 'desired at' option. this will set the
+ -- node.fixed member to true and also set node.pos.x and node.pos.y
+ self:fixateNodes(graph)
+
+ if #graph.nodes == 2 then
+ if not (graph.nodes[1].fixed and graph.nodes[2].fixed) then
+ local fixed_index = graph.nodes[2].fixed and 2 or 1
+ local loose_index = graph.nodes[2].fixed and 1 or 2
+
+ if not graph.nodes[1].fixed and not graph.nodes[2].fixed then
+ -- both nodes can be moved, so we assume node 1 is fixed at (0,0)
+ graph.nodes[1].pos.x = 0
+ graph.nodes[1].pos.y = 0
+ end
+
+ -- position the loose node relative to the fixed node, with
+ -- the displacement (random direction) matching the spring length
+ local direction = Vector.new{x = lib.random(1, 2), y = lib.random(1, 2)}
+ local distance = 3 * spring_length * self.graph_density * math.sqrt(self.graph_size) / 2
+ local displacement = direction:normalized():timesScalar(distance)
+
+ graph.nodes[loose_index].pos = graph.nodes[fixed_index].pos:plus(displacement)
+ else
+ -- both nodes are fixed, initial layout may be far from optimal
+ end
+ else
+ -- function to filter out fixed nodes
+ local function nodeNotFixed(node) return not node.fixed end
+
+ -- use the random positioning technique
+ local function positioning_func(n)
+ local radius = 3 * spring_length * self.graph_density * math.sqrt(self.graph_size) / 2
+ return lib.random(-radius, radius)
+ end
+
+ -- compute initial layout based on the random positioning technique
+ for _,node in ipairs(graph.nodes) do
+ if not node.fixed then
+ node.pos.x = positioning_func(1)
+ node.pos.y = positioning_func(2)
+ end
+ end
+ end
+end
+
+
+
+function SpringElectricalWalshaw2000:computeForceLayout(graph, spring_length)
+ -- global (=repulsive) force function
+ local function accurate_repulsive_force(distance, weight)
+ return - self.spring_constant * weight * math.pow(spring_length, self.repulsive_force_order + 1) / math.pow(distance, self.repulsive_force_order)
+ end
+
+ -- global (=repulsive, approximated) force function
+ local function approximated_repulsive_force(distance, mass)
+ return - mass * self.spring_constant * math.pow(spring_length, self.repulsive_force_order + 1) / math.pow(distance, self.repulsive_force_order)
+ end
+
+ -- local (spring) force function
+ local function attractive_force(distance, d, weight, charged, repulsive_force)
+ -- for charged nodes, never subtract the repulsive force; we want ALL other
+ -- nodes to be attracted more / repulsed less (not just non-adjacent ones),
+ -- depending on the charge of course
+ if charged then
+ return (distance - spring_length) / d - accurate_repulsive_force(distance, weight)
+ else
+ return (distance - spring_length) / d - (repulsive_force or 0)
+ end
+ end
+
+ -- define the Barnes-Hut opening criterion
+ function barnes_hut_criterion(cell, particle)
+ local distance = particle.pos:minus(cell.center_of_mass):norm()
+ return cell.width / distance <= 1.2
+ end
+
+ -- fixate all nodes that have a 'desired at' option. this will set the
+ -- node.fixed member to true and also set node.pos.x and node.pos.y
+ self:fixateNodes(graph)
+
+ -- adjust the initial step length automatically if desired by the user
+ local step_length = self.initial_step_length == 0 and spring_length or self.initial_step_length
+
+ -- convergence criteria
+ local converged = false
+ local i = 0
+
+ while not converged and i < self.iterations do
+
+ -- assume that we are converging
+ converged = true
+ i = i + 1
+
+ -- build the quadtree for approximating repulsive forces, if desired
+ local quadtree = nil
+ if self.approximate_repulsive_forces then
+ quadtree = self:buildQuadtree(graph)
+ end
+
+ local function nodeNotFixed(node) return not node.fixed end
+
+ -- iterate over all nodes
+ for _,v in ipairs(graph.nodes) do
+ if not v.fixed then
+ -- vector for the displacement of v
+ local d = Vector.new(2)
+
+ -- repulsive force induced by other nodes
+ local repulsive_forces = {}
+
+ -- compute repulsive forces
+ if self.approximate_repulsive_forces then
+ -- determine the cells that have an repulsive influence on v
+ local cells = quadtree:findInteractionCells(v, barnes_hut_criterion)
+
+ -- compute the repulsive force between these cells and v
+ for _,cell in ipairs(cells) do
+ -- check if the cell is a leaf
+ if #cell.subcells == 0 then
+ -- compute the forces between the node and all particles in the cell
+ for _,particle in ipairs(cell.particles) do
+ -- build a table that contains the particle plus all its subparticles
+ -- (particles at the same position)
+ local real_particles = lib.copy(particle.subparticles)
+ table.insert(real_particles, particle)
+
+ for _,real_particle in ipairs(real_particles) do
+ local delta = real_particle.pos:minus(v.pos)
+
+ -- enforce a small virtual distance if the node and the cell's
+ -- center of mass are located at (almost) the same position
+ if delta:norm() < 0.1 then
+ delta:update(function (n, value) return 0.1 + lib.random() * 0.1 end)
+ end
+
+ -- compute the repulsive force vector
+ local repulsive_force = approximated_repulsive_force(delta:norm(), real_particle.mass)
+ local force = delta:normalized():timesScalar(repulsive_force)
+
+ -- remember the repulsive force for the particle so that we can
+ -- subtract it later when computing the attractive forces with
+ -- adjacent nodes
+ repulsive_forces[real_particle.node] = repulsive_force
+
+ -- move the node v accordingly
+ d = d:plus(force)
+ end
+ end
+ else
+ -- compute the distance between the node and the cell's center of mass
+ local delta = cell.center_of_mass:minus(v.pos)
+
+ -- enforce a small virtual distance if the node and the cell's
+ -- center of mass are located at (almost) the same position
+ if delta:norm() < 0.1 then
+ delta:update(function (n, value) return 0.1 + lib.random() * 0.1 end)
+ end
+
+ -- compute the repulsive force vector
+ local repulsive_force = approximated_repulsive_force(delta:norm(), cell.mass)
+ local force = delta:normalized():timesScalar(repulsive_force)
+
+ -- TODO for each neighbor of v, check if it is in this cell.
+ -- if this is the case, compute the quadtree force for the mass
+ -- 'node.weight / cell.mass' and remember this as the repulsive
+ -- force of the neighbor; (it is not necessarily at
+ -- the center of mass of the cell, so the result is only an
+ -- approximation of the real repulsive force generated by the
+ -- neighbor)
+
+ -- move the node v accordingly
+ d = d:plus(force)
+ end
+ end
+ else
+ for _,u in ipairs(graph.nodes) do
+ if u.name ~= v.name then
+ -- compute the distance between u and v
+ local delta = u.pos:minus(v.pos)
+
+ -- enforce a small virtual distance if the nodes are
+ -- located at (almost) the same position
+ if delta:norm() < 0.1 then
+ delta:update(function (n, value) return 0.1 + lib.random() * 0.1 end)
+ end
+
+ -- compute the repulsive force vector
+ local repulsive_force = accurate_repulsive_force(delta:norm(), u.weight)
+ local force = delta:normalized():timesScalar(repulsive_force)
+
+ -- remember the repulsive force so we can later subtract them
+ -- when computing the attractive forces
+ repulsive_forces[u] = repulsive_force
+
+ -- move the node v accordingly
+ d = d:plus(force)
+ end
+ end
+ end
+
+ -- compute attractive forces between v and its neighbors
+ for _,edge in ipairs(v.edges) do
+ local u = edge:getNeighbour(v)
+
+ -- compute the distance between u and v
+ local delta = u.pos:minus(v.pos)
+
+ -- enforce a small virtual distance if the nodes are
+ -- located at (almost) the same position
+ if delta:norm() < 0.1 then
+ delta:update(function (n, value) return 0.1 + lib.random() * 0.1 end)
+ end
+
+ -- compute the spring force between them
+ local attr_force = attractive_force(delta:norm(), #v.edges, u.weight, u.charged, repulsive_forces[u])
+ local force = delta:normalized():timesScalar(attr_force)
+
+ -- move the node v accordingly
+ d = d:plus(force)
+ end
+
+ -- remember the previous position of v
+ old_position = v.pos:copy()
+
+ if d:norm() > 0 then
+ -- reposition v according to the force vector and the current temperature
+ v.pos = v.pos:plus(d:normalized():timesScalar(math.min(step_length, d:norm())))
+ end
+
+ -- we need to improve the system energy as long as any of
+ -- the node movements is large enough to assume we're far
+ -- away from the minimum system energy
+ if v.pos:minus(old_position):norm() > spring_length * self.convergence_tolerance then
+ converged = false
+ end
+ end
+ end
+
+ -- update the step length using the conservative cooling scheme
+ step_length = self.cooling_factor * step_length
+ end
+end
+
+
+
+-- Fixes nodes at their specified positions.
+--
+function SpringElectricalWalshaw2000:fixateNodes(graph)
+ local number_of_fixed_nodes = 0
+
+ for _,node in ipairs(graph.nodes) do
+ -- read the 'desired at' option of the node
+ local coordinate = node:getOption('desired at')
+
+ if coordinate then
+ -- parse the coordinate
+ node.pos.x = coordinate.x
+ node.pos.y = coordinate.y
+
+ -- mark the node as fixed
+ node.fixed = true
+
+ number_of_fixed_nodes = number_of_fixed_nodes + 1
+ end
+ end
+ if number_of_fixed_nodes > 1 then
+ self.growth_direction = "fixed" -- do not grow, orientation is now fixed
+ end
+end
+
+
+
+function SpringElectricalWalshaw2000:buildQuadtree(graph)
+ -- compute the minimum x and y coordinates of all nodes
+ local min_pos = graph.nodes[1].pos
+ for _,node in ipairs(graph.nodes) do
+ min_pos = Vector.new(2, function (n) return math.min(min_pos[n], node.pos[n]) end)
+ end
+
+ -- compute maximum x and y coordinates of all nodes
+ local max_pos = graph.nodes[1].pos
+ for _,node in ipairs(graph.nodes) do
+ max_pos = Vector.new(2, function (n) return math.max(max_pos[n], node.pos[n]) end)
+ end
+
+ -- make sure the maximum position is at least a tiny bit
+ -- larger than the minimum position
+ if min_pos:equals(max_pos) then
+ max_pos = max_pos:plus(Vector.new(2, function (n)
+ return 0.1 + lib.random() * 0.1
+ end))
+ end
+
+ -- make sure to make the quadtree area slightly larger than required
+ -- in theory; for some reason Lua will otherwise think that nodes with
+ -- min/max x/y coordinates are outside the box... weird? yes.
+ min_pos = min_pos:minusScalar(1)
+ max_pos = max_pos:plusScalar(1)
+
+ -- create the quadtree
+ quadtree = QuadTree.new(min_pos.x, min_pos.y,
+ max_pos.x - min_pos.x,
+ max_pos.y - min_pos.y)
+
+ -- insert nodes into the quadtree
+ for _,node in ipairs(graph.nodes) do
+ local particle = QuadTree.Particle.new(node.pos, node.weight)
+ particle.node = node
+ quadtree:insert(particle)
+ end
+
+ return quadtree
+end
+
+
+
+-- done
+
+return SpringElectricalWalshaw2000
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/force/SpringHu2006.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/force/SpringHu2006.lua
new file mode 100644
index 0000000000..86b65abc83
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/force/SpringHu2006.lua
@@ -0,0 +1,386 @@
+-- Copyright 2011 by Jannis Pohlmann
+-- Copyright 2012 by 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
+
+-- @release $Header$
+
+
+
+local SpringHu2006 = {}
+
+-- Imports
+local declare = require("pgf.gd.interface.InterfaceToAlgorithms").declare
+
+
+
+
+---
+
+declare {
+ key = "spring Hu 2006 layout",
+ algorithm = SpringHu2006,
+
+ preconditions = {
+ connected = true,
+ loop_free = true,
+ simple = true,
+ },
+
+ old_graph_model = true,
+
+ summary = [["
+ Implementation of a spring graph drawing algorithm based on
+ a paper by Hu.
+ "]],
+ documentation = [["
+ \begin{itemize}
+ \item
+ Y. Hu.
+ \newblock Efficient, high-quality force-directed graph drawing.
+ \newblock \emph{The Mathematica Journal}, 2006.
+ \end{itemize}
+
+ There are some modifications compared to the original algorithm,
+ see the Diploma thesis of Pohlmann for details.
+ "]]
+}
+
+
+-- Imports
+
+local PathLengths = require "pgf.gd.lib.PathLengths"
+local Vector = require "pgf.gd.deprecated.Vector"
+
+local CoarseGraph = require "pgf.gd.force.CoarseGraph"
+
+local lib = require("pgf.gd.lib")
+
+
+
+
+function SpringHu2006:run()
+
+ -- Setup some parameters
+ local options = self.digraph.options
+
+ self.iterations = options['iterations']
+ self.cooling_factor = options['cooling factor']
+ self.initial_step_length = options['initial step length']
+ self.convergence_tolerance = options['convergence tolerance']
+
+ self.natural_spring_length = options['node distance']
+
+ self.coarsen = options['coarsen']
+ self.downsize_ratio = options['downsize ratio']
+ self.minimum_graph_size = options['minimum coarsening size']
+
+
+ -- Setup
+
+ self.downsize_ratio = math.max(0, math.min(1, tonumber(self.downsize_ratio)))
+
+ self.graph_size = #self.graph.nodes
+ self.graph_density = (2 * #self.graph.edges) / (#self.graph.nodes * (#self.graph.nodes - 1))
+
+ -- validate input parameters
+ assert(self.iterations >= 0, 'iterations (value: ' .. self.iterations .. ') need to be greater than 0')
+ assert(self.cooling_factor >= 0 and self.cooling_factor <= 1, 'the cooling factor (value: ' .. self.cooling_factor .. ') needs to be between 0 and 1')
+ assert(self.initial_step_length >= 0, 'the initial step length (value: ' .. self.initial_step_length .. ') needs to be greater than or equal to 0')
+ assert(self.convergence_tolerance >= 0, 'the convergence tolerance (value: ' .. self.convergence_tolerance .. ') needs to be greater than or equal to 0')
+ assert(self.natural_spring_length >= 0, 'the natural spring dimension (value: ' .. self.natural_spring_length .. ') needs to be greater than or equal to 0')
+ assert(self.downsize_ratio >= 0 and self.downsize_ratio <= 1, 'the downsize ratio (value: ' .. self.downsize_ratio .. ') needs to be between 0 and 1')
+ assert(self.minimum_graph_size >= 2, 'the minimum coarsening size of coarse graphs (value: ' .. self.minimum_graph_size .. ') needs to be greater than or equal to 2')
+
+ -- initialize node weights
+ for _,node in ipairs(self.graph.nodes) do
+ node.weight = 1
+ end
+
+ -- initialize edge weights
+ for _,edge in ipairs(self.graph.edges) do
+ edge.weight = 1
+ end
+
+
+ -- initialize the coarse graph data structure. note that the algorithm
+ -- is the same regardless whether coarsening is used, except that the
+ -- number of coarsening steps without coarsening is 0
+ local coarse_graph = CoarseGraph.new(self.graph)
+
+ -- check if the multilevel approach should be used
+ if self.coarsen 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
+ while coarse_graph:getSize() > self.minimum_graph_size
+ and coarse_graph:getRatio() <= (1 - self.downsize_ratio)
+ do
+ coarse_graph:coarsen()
+ end
+ end
+
+ if self.coarsen then
+ -- use the natural spring length as the initial natural spring length
+ local spring_length = self.natural_spring_length
+
+ -- compute a random initial layout for the coarsest graph
+ self:computeInitialLayout(coarse_graph.graph, spring_length)
+
+ -- set the spring length to the average edge length of the initial layout
+ spring_length = 0
+ for _,edge in ipairs(coarse_graph.graph.edges) do
+ spring_length = spring_length + edge.nodes[1].pos:minus(edge.nodes[2].pos):norm()
+ end
+ spring_length = spring_length / #coarse_graph.graph.edges
+
+ -- 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 then
+ self:computeForceLayout(coarse_graph.graph, spring_length, SpringHu2006.adaptive_step_update)
+ end
+
+ -- undo coarsening step by step, applying the force-based sub-algorithm
+ -- to every intermediate coarse graph as well as the original graph
+ while coarse_graph:getLevel() > 0 do
+ -- compute the diameter of the parent coarse graph
+ local parent_diameter = PathLengths.pseudoDiameter(coarse_graph.graph)
+
+ -- interpolate the previous coarse graph from its parent
+ coarse_graph:interpolate()
+
+ -- compute the diameter of the current coarse graph
+ local current_diameter = PathLengths.pseudoDiameter(coarse_graph.graph)
+
+ -- scale node positions by the quotient of the pseudo diameters
+ for _,node in ipairs(coarse_graph.graph) do
+ node.pos:update(function (n, value)
+ return value * (current_diameter / parent_diameter)
+ end)
+ end
+
+ -- compute forces in the graph
+ self:computeForceLayout(coarse_graph.graph, spring_length, SpringHu2006.conservative_step_update)
+ end
+ else
+ -- compute a random initial layout for the coarsest graph
+ self:computeInitialLayout(coarse_graph.graph, self.natural_spring_length)
+
+ -- set the spring length to the average edge length of the initial layout
+ spring_length = 0
+ for _,edge in ipairs(coarse_graph.graph.edges) do
+ spring_length = spring_length + edge.nodes[1].pos:minus(edge.nodes[2].pos):norm()
+ end
+ spring_length = spring_length / #coarse_graph.graph.edges
+
+ -- improve the layout with the force-based algorithm
+ self:computeForceLayout(coarse_graph.graph, spring_length, SpringHu2006.adaptive_step_update)
+ end
+
+ local avg_spring_length = 0
+ for _,edge in ipairs(self.graph.edges) do
+ avg_spring_length = avg_spring_length + edge.nodes[1].pos:minus(edge.nodes[2].pos):norm()
+ end
+ avg_spring_length = avg_spring_length / #self.graph.edges
+end
+
+
+
+function SpringHu2006:computeInitialLayout(graph, spring_length)
+ -- TODO how can supernodes and fixed nodes go hand in hand?
+ -- maybe fix the supernode if at least one of its subnodes is
+ -- fixated?
+
+ -- fixate all nodes that have a 'desired at' option. this will set the
+ -- node.fixed member to true and also set node.pos.x and node.pos.y
+ self:fixateNodes(graph)
+
+ if #graph.nodes == 2 then
+ if not (graph.nodes[1].fixed and graph.nodes[2].fixed) then
+ local fixed_index = graph.nodes[2].fixed and 2 or 1
+ local loose_index = graph.nodes[2].fixed and 1 or 2
+
+ if not graph.nodes[1].fixed and not graph.nodes[2].fixed then
+ -- both nodes can be moved, so we assume node 1 is fixed at (0,0)
+ graph.nodes[1].pos.x = 0
+ graph.nodes[1].pos.y = 0
+ end
+
+ -- position the loose node relative to the fixed node, with
+ -- the displacement (random direction) matching the spring length
+ local direction = Vector.new{x = lib.random(1, spring_length), y = lib.random(1, spring_length)}
+ local distance = 1.8 * spring_length * self.graph_density * math.sqrt(self.graph_size) / 2
+ local displacement = direction:normalized():timesScalar(distance)
+
+ graph.nodes[loose_index].pos = graph.nodes[fixed_index].pos:plus(displacement)
+ else
+ -- both nodes are fixed, initial layout may be far from optimal
+ end
+ else
+ -- use a random positioning technique
+ local function positioning_func(n)
+ local radius = 2 * spring_length * self.graph_density * math.sqrt(self.graph_size) / 2
+ return lib.random(-radius, radius)
+ end
+
+ -- compute initial layout based on the random positioning technique
+ for _,node in ipairs(graph.nodes) do
+ if not node.fixed then
+ node.pos.x = positioning_func(1)
+ node.pos.y = positioning_func(2)
+ end
+ end
+ end
+end
+
+
+
+function SpringHu2006:computeForceLayout(graph, spring_length, step_update_func)
+ -- global (=repulsive) force function
+ function repulsive_force(distance, graph_distance, weight)
+ --return (1/4) * (1/math.pow(graph_distance, 2)) * (distance - (spring_length * graph_distance))
+ return (distance - (spring_length * graph_distance))
+ end
+
+ -- fixate all nodes that have a 'desired at' option. this will set the
+ -- node.fixed member to true and also set node.pos.x and node.pos.y
+ self:fixateNodes(graph)
+
+ -- adjust the initial step length automatically if desired by the user
+ local step_length = self.initial_step_length == 0 and spring_length or self.initial_step_length
+
+ -- convergence criteria etc.
+ local converged = false
+ local energy = math.huge
+ local iteration = 0
+ local progress = 0
+
+ -- compute graph distance between all pairs of nodes
+ local distances = PathLengths.floydWarshall(graph)
+
+ while not converged and iteration < self.iterations do
+ -- remember old node positions
+ local old_positions = lib.map(graph.nodes, function (node) return node.pos:copy(), node end)
+
+ -- remember the old system energy and reset it for the current iteration
+ local old_energy = energy
+ energy = 0
+
+ for _,v in ipairs(graph.nodes) do
+ if not v.fixed then
+ -- vector for the displacement of v
+ local d = Vector.new(2)
+
+ for _,u in ipairs(graph.nodes) do
+ if v ~= u then
+ -- compute the distance between u and v
+ local delta = u.pos:minus(v.pos)
+
+ -- enforce a small virtual distance if the nodes are
+ -- located at (almost) the same position
+ if delta:norm() < 0.1 then
+ delta:update(function (n, value) return 0.1 + lib.random() * 0.1 end)
+ end
+
+ local graph_distance = (distances[u] and distances[u][v]) and distances[u][v] or #graph.nodes + 1
+
+ -- compute the repulsive force vector
+ local force = repulsive_force(delta:norm(), graph_distance, v.weight)
+ local force = delta:normalized():timesScalar(force)
+
+ -- move the node v accordingly
+ d = d:plus(force)
+ end
+ end
+
+ -- really move the node now
+ -- TODO note how all nodes are moved by the same amount (step_length)
+ -- while Walshaw multiplies the normalized force with min(step_length,
+ -- d:norm()). could that improve this algorithm even further?
+ v.pos = v.pos:plus(d:normalized():timesScalar(step_length))
+
+ -- update the energy function
+ energy = energy + math.pow(d:norm(), 2)
+ end
+ end
+
+ -- update the step length and progress counter
+ step_length, progress = step_update_func(step_length, self.cooling_factor, energy, old_energy, progress)
+
+ -- compute the maximum node movement in this iteration
+ local max_movement = 0
+ for _,x in ipairs(graph.nodes) do
+ local delta = x.pos:minus(old_positions[x])
+ max_movement = math.max(delta:norm(), max_movement)
+ end
+
+ -- the algorithm will converge if the maximum movement is below a
+ -- threshold depending on the spring length and the convergence
+ -- tolerance
+ if max_movement < spring_length * self.convergence_tolerance then
+ converged = true
+ end
+
+ -- increment the iteration counter
+ iteration = iteration + 1
+ end
+end
+
+
+
+-- Fixes nodes at their specified positions.
+--
+function SpringHu2006:fixateNodes(graph)
+ local number_of_fixed_nodes = 0
+
+ for _,node in ipairs(graph.nodes) do
+ -- read the 'desired at' option of the node
+ local coordinate = node:getOption('desired at')
+
+ if coordinate then
+ -- apply the coordinate
+ node.pos.x = coordinate.x
+ node.pos.y = coordinate.y
+
+ -- mark the node as fixed
+ node.fixed = true
+
+ number_of_fixed_nodes = number_of_fixed_nodes + 1
+ end
+ end
+ if number_of_fixed_nodes > 1 then
+ self.growth_direction = "fixed" -- do not grow, orientation is now fixed
+ end
+end
+
+
+
+function SpringHu2006.conservative_step_update(step, cooling_factor)
+ return cooling_factor * step, nil
+end
+
+
+
+function SpringHu2006.adaptive_step_update(step, cooling_factor, energy, old_energy, progress)
+ if energy < old_energy then
+ progress = progress + 1
+ if progress >= 5 then
+ progress = 0
+ step = step / cooling_factor
+ end
+ else
+ progress = 0
+ step = cooling_factor * step
+ end
+ return step, progress
+end
+
+
+-- done
+
+return SpringHu2006
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/force/SpringLayouts.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/force/SpringLayouts.lua
new file mode 100644
index 0000000000..e3ac58d571
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/force/SpringLayouts.lua
@@ -0,0 +1,36 @@
+-- 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$
+
+
+-- Imports
+local declare = require("pgf.gd.interface.InterfaceToAlgorithms").declare
+
+
+---
+-- @section subsection {Spring Layouts}
+--
+-- @end
+
+
+
+---
+
+declare {
+ key = "spring layout",
+ use = {
+ { key = "spring Hu 2006 layout" },
+ },
+
+ summary = [["
+ This key selects Hu's 2006 spring layout with appropriate settings
+ for some parameters.
+ "]]
+}
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/force/jedi/algorithms/FruchtermanReingold.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/force/jedi/algorithms/FruchtermanReingold.lua
new file mode 100644
index 0000000000..2450bba2a2
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/force/jedi/algorithms/FruchtermanReingold.lua
@@ -0,0 +1,124 @@
+-- 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 to 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 influencing 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/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/force/jedi/algorithms/HuSpringElectricalFW.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/force/jedi/algorithms/HuSpringElectricalFW.lua
new file mode 100644
index 0000000000..57cd1547b6
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/force/jedi/algorithms/HuSpringElectricalFW.lua
@@ -0,0 +1,95 @@
+-- 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/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/force/jedi/algorithms/SimpleSpring.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/force/jedi/algorithms/SimpleSpring.lua
new file mode 100644
index 0000000000..4dbae2b1f4
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/force/jedi/algorithms/SimpleSpring.lua
@@ -0,0 +1,74 @@
+-- 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 attractive and repulsive 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 powerful 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/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/force/jedi/algorithms/SocialGravityCloseness.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/force/jedi/algorithms/SocialGravityCloseness.lua
new file mode 100644
index 0000000000..1c8a1bb8d9
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/force/jedi/algorithms/SocialGravityCloseness.lua
@@ -0,0 +1,129 @@
+-- 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
+ proportional 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
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/force/jedi/algorithms/SocialGravityDegree.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/force/jedi/algorithms/SocialGravityDegree.lua
new file mode 100644
index 0000000000..6408349107
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/force/jedi/algorithms/SocialGravityDegree.lua
@@ -0,0 +1,183 @@
+-- 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 proportional 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 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 =
+ [[
+ \tikz
+ \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 = [[
+ \tikz
+ \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/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/force/jedi/base/CoarseGraphFW.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/force/jedi/base/CoarseGraphFW.lua
new file mode 100644
index 0000000000..0487214654
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/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-vertices 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{graph} 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 associated with the current level, then
+-- updates the level.
+function CoarseGraph:uncoarsen()
+ local a = self.collapsed_vertices[self.level]
+ local ugraph = self.ugraph
+ local random = lib.random
+ local randomseed = lib.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 which 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 neighbor 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
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/force/jedi/base/ForceController.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/force/jedi/base/ForceController.lua
new file mode 100644
index 0000000000..ab30ada5b3
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/force/jedi/base/ForceController.lua
@@ -0,0 +1,489 @@
+-- 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 enhance
+-- 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 preprocessing 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 initial 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 = lib.random
+ local randomseed = lib.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
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/force/jedi/base/ForceTemplate.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/force/jedi/base/ForceTemplate.lua
new file mode 100644
index 0000000000..29370fc029
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/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/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/force/jedi/base/InitialTemplate.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/force/jedi/base/InitialTemplate.lua
new file mode 100644
index 0000000000..762717f64a
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/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/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/force/jedi/base/PathLengthsFW.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/force/jedi/base/PathLengthsFW.lua
new file mode 100644
index 0000000000..2d50677fa5
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/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 table 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/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/force/jedi/base/Preprocessing.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/force/jedi/base/Preprocessing.lua
new file mode 100644
index 0000000000..de59e0cb5c
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/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 precomputed 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
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/force/jedi/doc.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/force/jedi/doc.lua
new file mode 100644
index 0000000000..19baca7333
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/force/jedi/doc.lua
@@ -0,0 +1,377 @@
+-- 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 distance 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 movement 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/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/force/jedi/forcetypes/ForceAbsoluteValue.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/force/jedi/forcetypes/ForceAbsoluteValue.lua
new file mode 100644
index 0000000000..4634b70123
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/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$ coordinate
+
+-- 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 apply 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/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/force/jedi/forcetypes/ForceCanvasDistance.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/force/jedi/forcetypes/ForceCanvasDistance.lua
new file mode 100644
index 0000000000..8cea1aec6a
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/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 apply 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/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/force/jedi/forcetypes/ForceCanvasPosition.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/force/jedi/forcetypes/ForceCanvasPosition.lua
new file mode 100644
index 0000000000..41edaba358
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/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 apply 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/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/force/jedi/forcetypes/ForceGraphDistance.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/force/jedi/forcetypes/ForceGraphDistance.lua
new file mode 100644
index 0000000000..86d67f2678
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/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 and adding the effect to the passed net
+-- force array
+--
+-- @param data The parameters needed to apply 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/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/force/jedi/forcetypes/ForcePullToGrid.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/force/jedi/forcetypes/ForcePullToGrid.lua
new file mode 100644
index 0000000000..5f53f5da78
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/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 and adding the effect to the passed net
+-- force array
+--
+-- @param data The parameters needed to apply 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/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/force/jedi/forcetypes/ForcePullToPoint.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/force/jedi/forcetypes/ForcePullToPoint.lua
new file mode 100644
index 0000000000..985b8eec2e
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/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 and adding the effect to the passed net
+-- force array
+--
+-- @param data The parameters needed to apply 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/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/force/jedi/initialpositioning/CircularInitialPositioning.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/force/jedi/initialpositioning/CircularInitialPositioning.lua
new file mode 100644
index 0000000000..ab8f1fad9a
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/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 initial 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/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/force/jedi/initialpositioning/GridInitialPositioning.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/force/jedi/initialpositioning/GridInitialPositioning.lua
new file mode 100644
index 0000000000..2b131e8ad9
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/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 initial 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/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/force/jedi/initialpositioning/RandomInitialPositioning.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/force/jedi/initialpositioning/RandomInitialPositioning.lua
new file mode 100644
index 0000000000..ce3a40582c
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/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 initial 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 = lib.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
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/force/jedi/library.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/force/jedi/library.lua
new file mode 100644
index 0000000000..5d5554ce35
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/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 library requires all graph drawing algorithms and initial
+-- positioning algorithms provided with the first release of Jedi.
+-- It also defines the mass key attached to all vertices.
+
+-- Library name
+local jedi
+
+-- require initial 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/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/force/library.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/force/library.lua
new file mode 100644
index 0000000000..acfda34ffc
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/force/library.lua
@@ -0,0 +1,126 @@
+-- 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$
+
+
+
+---
+-- Nature creates beautiful graph layouts all the time. Consider a
+-- spider's web: Nodes are connected by edges in a visually most pleasing
+-- manner (if you ignore the spider in the middle). The layout of a
+-- spider's web is created just by the physical forces exerted by the
+-- threads. The idea behind force-based graph drawing algorithms is to
+-- mimic nature: We treat edges as threads that exert forces and simulate
+-- into which configuration the whole graph is ``pulled'' by these
+-- forces.
+--
+-- When you start thinking about for a moment, it turns out that there
+-- are endless variations of the force model. All of these models have
+-- the following in common, however:
+-- %
+-- \begin{itemize}
+-- \item ``Forces'' pull and push at the nodes in different directions.
+-- \item The effect of these forces is simulated by iteratively moving
+-- all the nodes simultaneously a little in the direction of the forces
+-- and by then recalculating the forces.
+-- \item The iteration is stopped either after a certain number of
+-- iterations or when a \emph{global energy minimum} is reached (a very
+-- scientific way of saying that nothing happens anymore).
+-- \end{itemize}
+--
+-- The main difference between the different force-based approaches is
+-- how the forces are determined. Here are some ideas what could cause a
+-- force to be exerted between two nodes (and there are more):
+-- %
+-- \begin{itemize}
+-- \item If the nodes are connected by an edge, one can treat the edge as
+-- a ``spring'' that has a ``natural spring dimension''. If the nodes
+-- are nearer than the spring dimension, they are push apart; if they
+-- are farther aways than the spring dimension, they are pulled together.
+-- \item If two nodes are connected by a path of a certain length, the
+-- nodes may ``wish to be at a distance proportional to the path
+-- length''. If they are nearer, they are pushed apart; if they are
+-- farther, they are pulled together. (This is obviously a
+-- generalization of the previous idea.)
+-- \item There may be a general force field that pushes nodes apart (an
+-- electrical field), so that nodes do not tend to ``cluster''.
+-- \item There may be a general force field that pulls nodes together (a
+-- gravitational field), so that nodes are not too loosely scattered.
+-- \item There may be highly nonlinear forces depending on the distance of
+-- nodes, so that nodes very near to each get pushed apart strongly,
+-- but the effect wears of rapidly at a distance. (Such forces are
+-- known as strong nuclear forces.)
+-- \item There rotational forces caused by the angles between the edges
+-- leaving a node. Such forces try to create a \emph{perfect angular
+-- resolution} (a very scientific way of saying that all angles
+-- at a node are equal).
+-- \end{itemize}
+--
+-- Force-based algorithms combine one or more of the above ideas into a
+-- single algorithm that uses ``good'' formulas for computing the
+-- forces.
+--
+-- Currently, three algorithms are implemented in this library, two of
+-- which are from the first of the following paper, while the third is
+-- from the third paper:
+-- %
+-- \begin{itemize}
+-- \item
+-- Y. Hu.
+-- \newblock Efficient, high-quality force-directed graph drawing.
+-- \newblock \emph{The Mathematica Journal}, 2006.
+-- \item
+-- C. Walshaw.
+-- \newblock A multilevel algorithm for force-directed graph
+-- drawing.
+-- \newblock In J. Marks, editor, \emph{Graph Drawing}, Lecture Notes in
+-- Computer Science, 1984:31--55, 2001.
+-- \end{itemize}
+--
+-- Our implementation is described in detail in the following
+-- diploma thesis:
+-- %
+-- \begin{itemize}
+-- \item
+-- Jannis Pohlmann,
+-- \newblock \emph{Configurable Graph Drawing Algorithms
+-- for the \tikzname\ Graphics Description Language,}
+-- \newblock Diploma Thesis,
+-- \newblock Institute of Theoretical Computer Science, Universit\"at
+-- zu L\"ubeck, 2011.\\[.5em]
+-- \newblock Online at
+-- \url{http://www.tcs.uni-luebeck.de/downloads/papers/2011/}\\ \url{2011-configurable-graph-drawing-algorithms-jannis-pohlmann.pdf}
+-- \end{itemize}
+--
+-- In the future, I hope that most, if not all, of the force-based
+-- algorithms become ``just configuration options'' of a general
+-- force-based algorithm similar to the way the modular Sugiyama method
+-- is implemented in the |layered| graph drawing library.
+--
+-- @library
+
+local force -- Library name
+
+-- Load declarations from:
+require "pgf.gd.force.ControlDeclare"
+require "pgf.gd.force.ControlStart"
+require "pgf.gd.force.ControlIteration"
+require "pgf.gd.force.ControlSprings"
+require "pgf.gd.force.ControlElectric"
+require "pgf.gd.force.ControlCoarsening"
+
+require "pgf.gd.force.SpringLayouts"
+require "pgf.gd.force.SpringElectricalLayouts"
+
+-- Load algorithms from:
+require "pgf.gd.force.SpringHu2006"
+require "pgf.gd.force.SpringElectricalHu2006"
+require "pgf.gd.force.SpringElectricalWalshaw2000"
+
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/interface.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/interface.lua
new file mode 100644
index 0000000000..df2649521b
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/interface.lua
@@ -0,0 +1,26 @@
+-- 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$
+
+
+
+-- Imports
+
+require "pgf"
+require "pgf.gd"
+
+
+-- Declare namespace
+pgf.gd.interface = {}
+
+
+-- Done
+
+return pgf.gd.interface \ No newline at end of file
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/interface/InterfaceCore.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/interface/InterfaceCore.lua
new file mode 100644
index 0000000000..8473eb3f10
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/interface/InterfaceCore.lua
@@ -0,0 +1,191 @@
+-- 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$
+
+
+
+---
+-- This class provides the core functionality of the interface between
+-- all the different layers (display layer, binding layer, and
+-- algorithm layer). The two classes |InterfaceToAlgorithms| and
+-- |InterfaceToDisplay| use, in particular, the data structures
+-- provided by this class.
+--
+-- @field binding This field stores the ``binding''. The graph drawing
+-- system is ``bound'' to the display layer through such a binding (a
+-- subclass of |Binding|). Such a binding can be thought of as a
+-- ``driver'' in operating systems terminology: It is a small set of
+-- functions needed to adapt the functionality to one specific display
+-- system. Note that the whole graph drawing scope is bound to exactly
+-- one display layer; to use several bindings you need to setup a
+-- completely new Lua instance.
+--
+-- @field scopes This is a stack of graph drawing scopes. All
+-- interface methods refer to the top of this stack.
+--
+-- @field collection_kinds This table stores which collection kinds
+-- have been defined together with their properties.
+--
+-- @field algorithm_classes A table that maps algorithm keys (like
+-- |tree layout| to class objects).
+--
+-- @field keys A lookup table of all declared keys. Each entry of this
+-- table consists of the original entry passed to the |declare|
+-- method. Each of these tables is both index at a number (so you can
+-- iterate over it using |ipairs|) and also via the key's name.
+
+local InterfaceCore = {
+ -- The main binding. Set by |InterfaceToDisplay.bind| method.
+ binding = nil,
+
+ -- The stack of Scope objects.
+ scopes = {},
+
+ -- The collection kinds.
+ collection_kinds = {},
+
+ -- The algorithm classes
+ algorithm_classes = {},
+
+ -- 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 = {
+ ["preprocessing stack"] = {},
+ ["edge routing stack"] = {},
+ ["postprocessing stack"] = {},
+ }
+ },
+ option_aliases = {
+ [{}] = true -- Remove, once Lua Link Bug is fixed
+ },
+
+ -- Constant strings for special collection kinds.
+ sublayout_kind = "INTERNAL_sublayout_kind",
+ subgraph_node_kind = "INTERNAL_subgraph_node_kind",
+}
+
+-- Namespace
+require("pgf.gd.interface").InterfaceCore = InterfaceCore
+
+
+InterfaceCore.option_initial.__index = InterfaceCore.option_initial
+InterfaceCore.option_initial.algorithm_phases.__index = InterfaceCore.option_initial.algorithm_phases
+
+
+-- Imports
+local Coordinate = require "pgf.gd.model.Coordinate"
+
+
+--- Returns the top scope
+--
+-- @return The current top scope, which is the scope in which
+-- everything should happen right now.
+
+function InterfaceCore.topScope()
+ return assert(InterfaceCore.scopes[#InterfaceCore.scopes], "no graph drawing scope open")
+end
+
+
+
+local factors = {
+ cm=28.45274,
+ mm=2.84526,
+ pt=1.0,
+ bp=1.00374,
+ sp=0.00002,
+ pc=12.0,
+ em=10,
+ ex=4.30554,
+ ["in"]=72.27,
+ dd=1.07,
+ cc=12.8401,
+ [""]=1,
+}
+
+local time_factors = {
+ s=1,
+ ms=0.001,
+ min=60,
+ h=3600
+}
+
+local directions = {
+ down = -90,
+ up = 90,
+ left = 180,
+ right = 0,
+ south = -90,
+ north = 90,
+ west = 180,
+ east = 0,
+ ["north east"] = 45,
+ ["north west"] = 135,
+ ["south east"] = -45,
+ ["south west"] = -135,
+ ["-"] = 0,
+ ["|"] = -90,
+}
+
+---
+-- Converts parameters types. This method is used by both the
+-- algorithm layer as well as the display layer to convert strings
+-- into the different types of parameters. When a parameter
+-- is pushed onto the option stack, you can either provide a value of
+-- the parameter's type; but you can also provide a string. This
+-- string can then be converted by this function to a value of the
+-- correct type.
+--
+-- @param s A parameter value or a string.
+-- @param t The type of the parameter
+--
+-- @return If |s| is not a string, it is just returned. If it is a
+-- string, it is converted to the type |t|.
+
+function InterfaceCore.convert(s,t)
+ if type(s) ~= "string" then
+ return s
+ elseif t == "number" then
+ return tonumber(s)
+ elseif t == "length" then
+ local num, dim = string.match(s, "([%d.]+)(.*)")
+ return tonumber(num) * assert(factors[dim], "unknown unit")
+ elseif t == "time" then
+ local num, dim = string.match(s, "([%d.]+)(.*)")
+ return tonumber(num) * assert(time_factors[dim], "unknown time unit")
+ elseif t == "string" then
+ return s
+ elseif t == "canvas coordinate" or t == "coordinate" then
+ local x, y = string.match(s,"%(([%d.]+)pt,([%d.]+)pt%)")
+ return Coordinate.new(tonumber(x),tonumber(y))
+ elseif t == "boolean" then
+ return s == "true"
+ elseif t == "raw" then
+ return loadstring(s)()
+ elseif t == "direction" then
+ return directions[s] or tonumber(s)
+ elseif t == "nil" or t == nil then
+ return nil
+ else
+ error ("unknown parameter type")
+ end
+end
+
+
+-- Done
+
+return InterfaceCore
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/interface/InterfaceToAlgorithms.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/interface/InterfaceToAlgorithms.lua
new file mode 100644
index 0000000000..1187fbc313
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/interface/InterfaceToAlgorithms.lua
@@ -0,0 +1,968 @@
+-- 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$
+
+
+
+---
+-- This class provides the interface between the graph drawing system
+-- and algorithms. Another class, |InterfaceToDisplay|, binds the
+-- display layers (like \tikzname\ or a graph drawing editor) to the
+-- graph drawing system ``from the other side''.
+--
+-- The functions declared here can be used by algorithms to
+-- communicate with the graph drawing system, which will usually
+-- forward the ``requests'' of the algorithms to the display layers in
+-- some way. For instance, when you declare a new parameter, this
+-- parameter will become available on the display layer.
+
+local InterfaceToAlgorithms = {}
+
+-- Namespace
+require("pgf.gd.interface").InterfaceToAlgorithms = InterfaceToAlgorithms
+
+
+-- Imports
+local InterfaceCore = require "pgf.gd.interface.InterfaceCore"
+local InterfaceToDisplay = require "pgf.gd.interface.InterfaceToDisplay"
+local InterfaceToC = require "pgf.gd.interface.InterfaceToC"
+
+local LookupTable = require "pgf.gd.lib.LookupTable"
+local LayoutPipeline = require "pgf.gd.control.LayoutPipeline"
+
+local Edge = require "pgf.gd.model.Edge"
+
+local lib = require "pgf.gd.lib"
+
+local doc = require "pgf.gd.doc"
+
+-- Forwards
+
+local declare_handlers
+
+
+
+
+---
+-- Adds a handler for the |declare| function. The |declare|
+-- command is just a ``dispatcher'' to one of many possible
+-- declaration functions. Which function is used, depends on which
+-- fields are present in the table passed to |declare|. For each
+-- registered handler, we call the |test| function. If it returns
+-- neither |nil| nor |false|, the |handler| field of this handler is
+-- called. If it returns |true|, the handler immediately
+-- finishes. Otherwise, the next handler is tried.
+
+function InterfaceToAlgorithms.addHandler(test, handler)
+ table.insert(declare_handlers, 1, { test = test, handler = handler })
+end
+
+
+
+-- Local stuff
+
+local key_metatable = {}
+
+---
+-- This function is the ``work-horse'' for declaring things. It allows
+-- you to specify on the algorithmic layer that a key ``is available''
+-- for use on the display layer. There is just one function for
+-- handling all declarations in order to make the declarations
+-- easy-to-use since you just need to import a single function:
+-- %
+--\begin{codeexample}[code only, tikz syntax=false]
+--local declare = require "pgf.gd.interface.InterfaceToAlgorithms".declare
+--\end{codeexample}
+--
+-- You can now use |declare| it as follows: You pass it a table
+-- containing information about the to-be-declared key. The table
+-- \emph{must} have a field |key| whose value is unique and must be a
+-- string. If the value of |key| is, say, |"foo"|, the
+-- parameter can be set on the display layer such as, say, the
+-- \tikzname\ layer, using |/graph drawing/foo|. Here is a typical
+-- example of how a declaration is done:
+-- %
+--\begin{codeexample}[code only, tikz syntax=false]
+-- ---
+-- declare {
+-- key = "electrical charge",
+-- type = "number",
+-- initial = "1.0",
+--
+-- summary = "The ``electrical charge'' is a property...",
+-- documentation = [[...]],
+-- examples = [[...]]
+-- }
+--\end{codeexample}
+--
+-- \medskip\noindent\textbf{Inlining Documentation.}
+-- The three keys |summary|, |documentation| and |examples| are
+-- intended for the display layer to give the users information about
+-- what the key does. The |summary| should be a string that succinctly
+-- describes the option. This text will typically be displayed for
+-- instance as a ``tool tip'' or in an option overview. The
+-- |documentation| optionally provides more information and should be
+-- typeset using \TeX. The |examples| can either be a single string or
+-- an array of strings. Each should be a \tikzname\ example
+-- demonstrating how the key is used.
+--
+-- Note that you can take advantage of the Lua syntax of enclosing
+-- very long multi-line strings in |[[| and |]]|. As a bonus, if the
+-- summary, documentation, or an example starts and ends with a quote,
+-- these two quotes will be stripped. This allows you to enclose the
+-- whole multi-line string (additionally) in quotes, leading to better
+-- syntax highlighting in editors.
+--
+-- \medskip\noindent\textbf{External Documentation.}
+-- It is sometimes more desirable to put the documentation of a key
+-- into an external file. First, this makes the code leaner and, thus,
+-- faster to read (both for humans and for computers). Second, for C
+-- code, it is quite inconvenient to have long strings inside a C
+-- file. In such cases, you can use the |documentation_in| field:
+-- %
+--\begin{codeexample}[code only, tikz syntax=false]
+-- ---
+-- declare {
+-- key = "electrical charge",
+-- type = "number",
+-- initial = "1.0",
+-- documentation_in = "some_filename"
+-- }
+--\end{codeexample}
+--
+-- The |some_filename| must be the name of a Lua file that will be
+-- read ``on demand'', that is, whenever someone tries to access the
+-- documentation, summary, or examples field of the key, this file
+-- will be loaded using |require|. The file should then use
+-- |pgf.gd.doc| to install the missing information in the keys.
+--
+-- \medskip\noindent\textbf{The Use Field.}
+-- When you declare a key, you can provide a |use| field. If present,
+-- you must set it to an array of small tables which have two fields:
+-- %
+-- \begin{itemize}
+-- \item |key| This is the name of another key or a function.
+-- \item |value| This is either a value (like a string or a number) or
+-- a function or |nil|.
+-- \end{itemize}
+--
+-- Here is an example:
+-- %
+--\begin{codeexample}[code only, tikz syntax=false]
+-- ---
+-- declare {
+-- key = "binary tree layout",
+-- use = {
+-- { key = "minimum number of children", value = 2 },
+-- { key = "significant sep", value = 12 },
+-- { key = "tree layout" }
+-- },
+-- summary = "The |binary tree layout| places node...",
+-- documentation = ...,
+-- examples = ...,
+-- }
+--\end{codeexample}
+--
+-- The effect of a |use| field is the following: Whenever the key is
+-- encountered on the option stack, the key is first handled
+-- normally. Then, we iterate over all elements of the |use|
+-- array. For each element, we perform the action as if the |key| of
+-- the array had been set explicitly to the value given by the |value|
+-- field. If the |value| is a function, we pass a different value to
+-- the key, namely the result of applying the function to the value
+-- originally passed to the original key. Here is a typical example:
+-- %
+--\begin{codeexample}[code only, tikz syntax=false]
+-- ---
+-- declare {
+-- key = "level sep",
+-- type = "length",
+-- use = {
+-- { key = "level pre sep", value = function (v) return v/2 end },
+-- { key = "level post sep", value = function (v) return v/2 end }
+-- },
+-- summary = "..."
+-- }
+--\end{codeexample}
+--
+-- Just like the value, the key itself can also be a function. In this
+-- case, the to-be-used key is also computed by applying the function
+-- to the value passed to the original key.
+--
+-- As mentioned at the beginning, |declare| is a work-horse that will call
+-- different internal functions depending on whether you declare a
+-- parameter key or a new algorithm or a collection kind. Which kind
+-- of declaration is being done is detected by the presence of certain
+-- fields in the table passed to |t|. The different kind of
+-- possible declarations are documented in the |declare_...|
+-- functions. Note that these functions are internal and cannot be
+-- called from outside; you must use the |declare| function.
+--
+-- @param t A table contain the field |key| and other fields as
+-- described.
+
+function InterfaceToAlgorithms.declare (t)
+ local keys = InterfaceCore.keys
+
+ -- Sanity check:
+ assert (type(t.key) == "string" and t.key ~= "", "parameter key may not be the empty string")
+ if keys[t.key] or t.keys == "algorithm_phases" then
+ error("parameter '" .. t.key .. "' already declared")
+ end
+
+ for _,h in ipairs (declare_handlers) do
+ if h.test(t) then
+ if h.handler(t) then
+ break
+ end
+ end
+ end
+
+ -- Attach metatable:
+ setmetatable (t, key_metatable)
+
+ -- Set!
+ keys[t.key] = t
+ keys[#keys + 1] = t
+end
+
+
+function key_metatable.__index (key_table, what)
+ if what == "documentation" or what == "summary" or what == "examples" then
+ local doc = rawget(key_table,"documentation_in")
+ if doc then
+ require (doc)
+ return rawget(key_table, what)
+ end
+ end
+end
+
+
+
+---
+-- This function is called by |declare| for ``normal parameter keys'',
+-- which are all keys for which no special field like |algorithm| or
+-- |layer| is declared. You write
+-- %
+--\begin{codeexample}[code only, tikz syntax=false]
+-- ---
+-- declare {
+-- key = "electrical charge",
+-- type = "number",
+-- initial = "1.0",
+--
+-- summary = "The ``electrical charge'' is a property...",
+-- documentation = [[...]],
+-- examples = [[...]]
+-- }
+--\end{codeexample}
+--
+-- When an author writes |my node[electrical charge=5-3]| in the
+-- description of her graph, the object |vertex| corresponding to the
+-- node |my node| will have a field |options| attached to it with
+-- %
+--\begin{codeexample}[code only, tikz syntax=false]
+--vertex.options["electrical charge"] == 2
+--\end{codeexample}
+--
+-- The |type| field does not refer to Lua types. Rather, these types are
+-- sensible types for graph drawing and they are mapped by the higher
+-- layers to Lua types. In detail, the following types are available:
+-- %
+-- \begin{itemize}
+-- \item |number| A dimensionless number. Will be mapped to a normal
+-- Lua |number|. So, when the author writes |foo=5*2|, the |foo| key
+-- of the |options| field of the corresponding object will be set to
+-- |10.0|.
+-- \item |length| A ``dimension'' in the sense of \TeX\ (a number with
+-- a dimension like |cm| attached to it). It is the job of the display
+-- layer to map this to a number in ``\TeX\ points'', that is, to a
+-- multiple of $1/72.27$th of an inch.
+-- \item |time| A ``time'' in the sense of |\pgfparsetime|. Examples
+-- are |6s| or |0.1min| or |6000ms|, all of which will map to |6|.
+-- \item |string| Some text. Will be mapped to a Lua |string|.
+-- \item |canvas coordinate| A position on the canvas. Will be mapped
+-- to a |model.Coordinate|.
+-- \item |boolean| A Boolean value.
+-- \item |raw| Some to-be-executed Lua text.
+-- \item |direction| Normally, an angle; however,
+-- the special values of |down|, |up|, |left|, |right| as well as the
+-- directions |north|, |north west|, and so on are also legal on the
+-- display layer. All of them will be mapped to a number. Furthermore,
+-- a vertical bar (\verb!|!) will be mapped to |-90| and a minus sign
+-- (|-|) will be mapped to |0|.
+-- \item |hidden| A key of this type ``cannot be set'', that is,
+-- users cannot set this key at all. However algorithms can still read
+-- this key and, through the use of |alias|, can use the key as a
+-- handle to another key.
+-- \item |user value| The key stores a Lua user value (userdata). Such
+-- keys can only be set from C since user values cannot be created in
+-- Lua (let alone in \tikzname).
+-- \end{itemize}
+--
+-- If the |type| field is missing, it is automatically set to
+-- |"string"|.
+--
+-- A parameter can have an |initial| value. This value will be used
+-- whenever the parameter has not been set explicitly for an object.
+--
+-- A parameter can have a |default| value. This value will be used as
+-- the parameter value whenever the parameter is explicitly set, but
+-- no value is provided. For a key of type |"boolean"|, if no
+-- |default| is provided, |"true"| will be used automatically.
+--
+-- A parameter can have an |alias| field. This field must be set to
+-- the name of another key or to a function. Whenever you access the
+-- current key and this key is not set, the |alias| key is tried
+-- instead. If it is set, its value will be returned (if the |alias|
+-- key has itself an alias set, this is tried recursively). If the
+-- alias is not set either and neither does it have an initial value,
+-- the |initial| value is used. Note that in case the alias has its
+-- |initial| field set, the |initial| value of the current key will
+-- never be used.
+--
+-- The main purpose of the current key is to allow algorithms to
+-- introduce their own terminology for keys while still having access
+-- to the standard keys. For instance, the |OptimalHierarchyLayout|
+-- class uses the name |layerDistance| for what would be called
+-- |level distance| in the rest of the graph drawing system. In this
+-- case, we can declare the |layerDistance| key as follows:
+-- %
+--\begin{codeexample}[code only, tikz syntax=false]
+-- declare {
+-- key = "layerDistance",
+-- type = "length",
+-- alias = "level distance"
+-- }
+--\end{codeexample}
+--
+-- Inside the algorithm, we can write |...options.layerDistance| and
+-- will get the current value of the |level distance| unless the
+-- |layerDistance| has been set explicitly. Indeed, we might set the
+-- |type| to |hidden| to ensure that \emph{only} the |level distance|
+-- can and must set to set the layerDistance.
+--
+-- Note that there is a difference between |alias| and the |use|
+-- field: Suppose we write
+-- %
+--\begin{codeexample}[code only, tikz syntax=false]
+-- declare {
+-- key = "layerDistance",
+-- type = "length",
+-- use = {
+-- { key = "level distance", value = lib.id }
+-- }
+-- }
+--\end{codeexample}
+--
+-- Here, when you say |layerDistance=1cm|, the |level distance| itself
+-- will be modified. When the |level distance| is set, however, the
+-- |layerDistance| will not be modified.
+--
+-- If the alias is a function, it will be called with the option table
+-- as its parameter. You can thus say things like
+-- %
+--\begin{codeexample}[code only, tikz syntax=false]
+-- declare {
+-- key = "layerDistance",
+-- type = "length",
+-- alias = function (option)
+-- return option["layer pre dist"] + option["layer post dist"]
+-- end
+-- }
+--\end{codeexample}
+--
+-- As a special courtesy to C code, you can also set the key
+-- |alias_function_string|, which allows you to put the function into
+-- a string that is read using |loadstring|.
+--
+-- (You cannot call this function directly, it is included for
+-- documentation purposes only.)
+--
+-- @param t The table originally passed to |declare|.
+
+local function declare_parameter (t)
+
+ t.type = t.type or "string"
+
+ if t.type == "boolean" and t.default == nil then
+ t.default = true
+ end
+
+ -- Normal key
+ assert (type(t.type) == "string", "key type must be a string")
+
+ -- Declare via the hub:
+ if t.type ~= "hidden" then
+ InterfaceCore.binding:declareCallback(t)
+
+ -- Handle initials:
+ if t.initial then
+ InterfaceCore.option_initial[t.key] = InterfaceCore.convert(t.initial, t.type)
+ end
+ end
+
+ if t.alias_function_string and not t.alias then
+ local count = 0
+ t.alias = load (
+ function ()
+ count = count + 1
+ if count == 1 then
+ return "return "
+ elseif count == 2 then
+ return t.alias_function_string
+ else
+ return nil
+ end
+ end)()
+ end
+
+ if t.alias then
+ assert (type(t.alias) == "string" or type(t.alias == "function"), "alias must be a string or a function")
+ InterfaceCore.option_aliases[t.key] = t.alias
+ end
+
+ return true
+end
+
+
+
+
+---
+-- This function is called by |declare| for ``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]
+-- local ReingoldTilford1981 = {}
+--
+-- ---
+-- declare {
+-- key = "tree layout",
+-- algorithm = ReingoldTilford1981,
+--
+-- preconditions = {
+-- connected = true,
+-- tree = true
+-- },
+--
+-- postconditions = {
+-- upward_oriented = true
+-- },
+--
+-- summary = "The Reingold--Tilford method is...",
+-- documentation = ...,
+-- examples = ...,
+-- }
+--
+-- function ReingoldTilford1981:run()
+-- ...
+-- end
+--\end{codeexample}
+--
+-- The |algorithm| field expects either a table or a string as
+-- value. If you provide a string, then |require| will be applied to
+-- this string to obtain the table; however, this will happen only
+-- when the key is actually used for the first time. This means that
+-- you can declare (numerous) algorithms in a library without these
+-- algorithms actually being loaded until they are needed.
+--
+-- Independently of how the table is obtained, it will be ``upgraded''
+-- to a class by setting its |__index| field and installing a static
+-- |new| function (which takes a table of initial values as
+-- argument). Both these settings will only be done if they have not
+-- yet been performed.
+--
+-- Next, you can specify the fields |preconditions| and
+-- |postconditions|. The preconditions are a table that tell the graph
+-- drawing engine what kind of graphs your algorithm expects. If the
+-- input graph is not of this kind, it will be automatically
+-- transformed to meet this condition. Similarly, the postconditions
+-- tell the engine about properties of your graph after the algorithm
+-- has run. Again, additional transformations may be performed.
+--
+-- You can also specify the field |phase|. This tells the graph
+-- 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.
+-- 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.
+--
+-- 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 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"|:
+-- %
+--\begin{codeexample}[code only, tikz syntax=false]
+-- ---
+-- declare {
+-- key = "breadth first spanning tree",
+-- algorithm = {
+-- run =
+-- function (self)
+-- return SpanningTreeComputation.computeSpanningTree(self.ugraph, false, self.events)
+-- end
+-- },
+-- phase = "spanning tree computation",
+-- phase_default = true,
+-- summary = ...
+-- }
+--\end{codeexample}
+--
+-- 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"]
+-- local spanning_algorithm =
+-- spanning_algorithm_class.new{
+-- ugraph = ugraph,
+-- events = scope.events
+-- }
+-- local spanning_tree = spanning_algorithm:run()
+--\end{codeexample}
+--
+-- If you set the |phase_default| field of |t| to |true|, the algorithm will
+-- 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). 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.)
+--
+-- @param t The table originally passed to |declare|.
+
+local function declare_algorithm (t)
+ -- Algorithm declaration!
+ assert(type(t.algorithm) == "table" or type(t.algorithm) == "string")
+
+ t.phase = t.phase or "main"
+
+ local function make_class ()
+ local class
+
+ if type(t.algorithm) == "table" then
+ class = lib.class(t.algorithm)
+ else
+ class = lib.class(require(t.algorithm))
+ end
+
+ -- Now, save pre- and postconditions
+ class.preconditions = t.preconditions or {}
+ class.postconditions = t.postconditions or {}
+
+ -- Save phase
+ class.phase = t.phase
+
+ -- Compatibility
+ class.old_graph_model = t.old_graph_model
+
+ return class
+ end
+
+ -- Store this:
+ local store_me
+ if type(t.algorithm) == "table" then
+ store_me = make_class()
+ else
+ store_me = make_class
+ end
+
+ -- 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)
+
+ if t.phase_default then
+ assert (not InterfaceCore.option_initial.algorithm_phases[t.phase],
+ "default algorithm for phase already set")
+ 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
+end
+
+
+
+
+---
+-- This function is called by |declare| for ``collection kinds''. They
+-- are detected by the presence of the field |layer|
+-- in the table |t| passed to |declare|. See the class |Collection|
+-- for details on what a collection and a collection kind is.
+--
+-- The |key| field of the table |t| passed to this function is both
+-- the name of the to-be-declared collection kind as well as the key
+-- that is used on the display layer to indicate that a node or edge
+-- belongs to a collection.
+--
+-- \medskip
+-- \noindent\textbf{The Display Layer.}
+-- Let us first have a look at what happens on the display layer:
+-- A key |t.key| is setup on the display layer that, when used inside
+-- a graph drawing scope, starts a new collection of the specified
+-- kind. ``Starts'' means that all nodes and edges mentioned in the
+-- rest of the current option scope will belong to a new collection
+-- of kind |t.key|.
+-- %
+--\begin{codeexample}[code only, tikz syntax=false]
+--declare { key = "hyper", layer = 1 }
+--\end{codeexample}
+-- %
+-- you can say on the \tikzname\ layer
+-- %
+--\begin{codeexample}[code only]
+-- \graph {
+-- a, b, c, d;
+-- { [hyper] a, b, c }
+-- { [hyper] b, c, d }
+-- };
+--\end{codeexample}
+--
+-- In this case, the nodes |a|, |b|, |c| will belong to a collection of
+-- kind |hyper|. The nodes |b|, |c|, and |d| will (also) belong to
+-- another collection of the same kind |hyper|. You can nest
+-- collections; in this case, nodes will belong to several
+-- collections.
+--
+-- The effect of declaring a collection kind on the algorithm layer
+-- it, first of all, that |scope.collections| will have a field named
+-- by the collection kind. This field will store an array that
+-- contains all collections that were declared as part of the
+-- graph. For instance, |collections.hyper| will contain all
+-- hyperedges, each of which is a table with the following fields: The
+-- |vertices| and |edges| fields each contain arrays of all objects
+-- being part of the collection. The |sub| field is an array of
+-- ``subcollections'', that is, all collections that were started
+-- inside another collection. (For the collection kinds |hyper| and
+-- |same layer| this makes no sense, but subgraphs could, for instance,
+-- be nested.)
+--
+-- \medskip
+-- \noindent\textbf{Rendering of Collections.}
+-- For some kinds of collections, it makes sense to \emph{render} them,
+-- but only after the graph drawing algorithm has run. For this
+-- purpose, the binding layer will use a callback for each collection
+-- kind and each collection, see the |Binding| class for details.
+-- Suppose, for instance, you would
+-- like hyperedges to be rendered. In this case, a graph drawing
+-- algorithm should iterate over all collections of type |hyper| and
+-- compute some hints on how to render the hyperedge and store this
+-- information in the |generated_options| table of the hyperedge. Then,
+-- the binding layer will ask the display layer to run some some code
+-- that is able to read key--value pairs passed to
+-- it (which are the key--value pairs of the |generated_options| table)
+-- and use this information to nicely draw the hyperedge.
+--
+-- The number |t.layer| determines in which order the different
+-- collection kinds are rendered.
+--
+-- The last parameter, the layer number, is used to specify the order
+-- in which the different collection kinds are rendered. The higher the
+-- number, the later the collection will be rendered. Thus, if there is
+-- a collection kind with layer number 10 and another with layer number
+-- 20, all collections of the first kind will be rendered first,
+-- followed by all collections of the second kind.
+--
+-- Collections whose layer kinds are non-negative get rendered
+-- \emph{after} the nodes and edges have already been rendered. In
+-- contrast, collections with a negative layer number get shown
+-- ``below'' the nodes and edges.
+--
+-- (You cannot call this function directly, it is included for
+-- documentation purposes only.)
+--
+-- @param t The table originally passed to |declare|.
+
+local function declare_collection_kind (t)
+ assert (type(t.layer) == "number", "layer must be a number")
+
+ local layer = t.layer
+ local kind = t.key
+ local kinds = InterfaceCore.collection_kinds
+ local new_entry = { kind = kind, layer = layer }
+
+ -- Insert into table part:
+ kinds[kind] = new_entry
+
+ -- Insert into array part:
+ local found
+ for i=1,#kinds do
+ if kinds[i].layer > layer or (kinds[i].layer == layer and kinds[i].kind > kind) then
+ table.insert(kinds, i, new_entry)
+ return
+ end
+ end
+
+ kinds[#kinds+1] = new_entry
+
+ -- Bind
+ InterfaceCore.binding:declareCallback(t)
+
+ return true
+end
+
+
+
+-- Build in handlers:
+
+declare_handlers = {
+ { test = function (t) return t.algorithm_written_in_c end, handler = InterfaceToC.declare_algorithm_written_in_c },
+ { test = function (t) return t.algorithm end, handler = declare_algorithm },
+ { test = function (t) return t.layer end, handler = declare_collection_kind },
+ { test = function (t) return true end, handler = declare_parameter }
+}
+
+
+
+
+
+
+
+
+---
+-- Finds a node by its name. This method should be used by algorithms
+-- for which a node name is specified in some option and, thus, needs
+-- to be converted to a vertex object during a run of the algorithm.
+--
+-- @param name A node name
+--
+-- @return The vertex of the given name in the syntactic digraph or
+-- |nil|.
+
+function InterfaceToAlgorithms.findVertexByName(name)
+ return InterfaceCore.topScope().node_names[name]
+end
+
+
+
+
+
+-- Helper function
+local function add_to_collections(collection,where,what)
+ if collection then
+ LookupTable.addOne(collection[where],what)
+ add_to_collections(collection.parent,where,what)
+ end
+end
+
+local unique_count = 1
+
+---
+-- Generate a new vertex in the syntactic digraph. Calling this method
+-- allows algorithms to create vertices that are not present in the
+-- original input graph. Using the graph drawing coroutine, this
+-- function will pass back control to the display layer in order to
+-- render the vertex and, thereby, create precise size information
+-- about it.
+--
+-- Note that creating new vertices in the syntactic digraph while the
+-- algorithm is already running is a bit at odds with the notion of
+-- treating graph drawing as a series of graph transformations: For
+-- instance, when a new vertex is created, the graph will (at least
+-- temporarily) no longer be connected; even though an algorithm may
+-- have requested that it should only be fed connected
+-- graphs. Likewise, more complicated requirements like insisting on
+-- the graph being a tree also cannot be met.
+--
+-- For these reasons, the following happens, when a new vertex is
+-- created using the function:
+-- %
+-- \begin{enumerate}
+-- \item The vertex is added to the syntactic digraph.
+-- \item It is added to all layouts on the current layout stack. When
+-- a graph drawing algorithm is run, it is not necessarily run on the
+-- original syntactic digraph. Rather, a sequence / stack of nested
+-- layouts may currently
+-- be processed and the vertex is added to all of them.
+-- \item The vertex is added to both the |digraph| and the |ugraph| of
+-- the current algorithm.
+-- \end{enumerate}
+--
+-- @param algorithm An algorithm for whose syntactic digraph the node
+-- should be added
+-- @param init A table of initial values for the node that is passed
+-- to |Binding:createVertex|, see that function for details.
+--
+-- @return The newly created node
+--
+function InterfaceToAlgorithms.createVertex(algorithm, init)
+
+ -- Setup
+ local scope = InterfaceCore.topScope()
+ local binding = InterfaceCore.binding
+
+ -- Setup node
+ if not init.name then
+ init.name = "internal@gd@node@" .. unique_count
+ unique_count = unique_count + 1
+ end
+
+ -- Does vertex already exist?
+ assert (not scope.node_names[name], "node already created")
+
+ if not init.shape or init.shape == "none" then
+ init.shape = "rectangle"
+ end
+
+ -- Call binding
+ binding:createVertex(init)
+
+ local v = assert(scope.node_names[init.name], "internal node creation failed")
+
+ -- Add vertex to the algorithm's digraph and ugraph
+ algorithm.syntactic_component:add {v}
+ algorithm.digraph:add {v}
+ algorithm.ugraph:add {v}
+
+ -- Compute bounding boxes:
+ LayoutPipeline.prepareBoundingBoxes(algorithm.rotation_info, algorithm.adjusted_bb, algorithm.digraph, {v})
+
+ -- Add the node to the layout stack:
+ add_to_collections(algorithm.layout, "vertices", v)
+
+ algorithm.layout_graph:add { v }
+
+ return v
+end
+
+
+
+---
+-- Generate a new edge in the syntactic digraph. This method is quite
+-- similar to |createVertex| and has the same effects with respect to
+-- the edge: The edge is added to the syntactic digraph and also to
+-- all layouts on the layout stack. Furthermore, appropriate edges are
+-- added to the |digraph| and the |ugraph| of the algorithm currently
+-- running.
+--
+-- @param algorithm An algorithm for whose syntactic digraph the node should be added
+-- @param tail A syntactic tail vertex
+-- @param head A syntactic head vertex
+-- @param init A table of initial values for the edge.
+--
+-- The following fields are useful for |init|:
+-- %
+-- \begin{itemize}
+-- \item |init.direction| If present, a direction for the edge. Defaults to "--".
+-- \item |init.options| If present, some options for the edge.
+-- \item |init.generated_options| A table that is passed back to the
+-- display layer as a list of key-value pairs in the syntax of
+-- |declare_parameter|.
+-- \end{itemize}
+
+function InterfaceToAlgorithms.createEdge(algorithm, tail, head, init)
+
+ init = init or {}
+
+ -- Setup
+ local scope = InterfaceCore.topScope()
+ local binding = InterfaceCore.binding
+ local syntactic_digraph = algorithm.layout_graph
+ local syntactic_component = algorithm.syntactic_component
+
+ assert (syntactic_digraph:contains(tail) and
+ syntactic_digraph:contains(head),
+ "attempting to create edge between nodes that are not in the syntactic digraph")
+
+ local arc = syntactic_digraph:connect(tail, head)
+
+ local edge = Edge.new {
+ head = head,
+ tail = tail,
+ direction = init.direction or "--",
+ options = init.options or algorithm.layout.options,
+ path = init.path,
+ generated_options = init.generated_options
+ }
+
+ -- Add to arc
+ arc.syntactic_edges[#arc.syntactic_edges+1] = edge
+
+ local s_arc = syntactic_component:connect(tail, head)
+ s_arc.syntactic_edges = arc.syntactic_edges
+
+ -- Create Event
+ local e = InterfaceToDisplay.createEvent ("edge", { arc, #arc.syntactic_edges })
+ edge.event = e
+
+ -- Make part of collections
+ for _,c in ipairs(edge.options.collections) do
+ LookupTable.addOne(c.edges, edge)
+ end
+
+ -- Call binding
+ binding.storage[edge] = {}
+ binding:everyEdgeCreation(edge)
+
+ -- Add edge to digraph and ugraph
+ local direction = edge.direction
+ if direction == "->" then
+ algorithm.digraph:connect(tail, head)
+ elseif direction == "<-" then
+ algorithm.digraph:connect(head, tail)
+ elseif direction == "--" or direction == "<->" then
+ algorithm.digraph:connect(tail, head)
+ algorithm.digraph:connect(head, tail)
+ end
+ algorithm.ugraph:connect(tail, head)
+ algorithm.ugraph:connect(head, tail)
+
+ -- Add edge to layouts
+ add_to_collections(algorithm.layout, "edges", edge)
+
+end
+
+
+
+
+
+-- Done
+
+return InterfaceToAlgorithms
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/interface/InterfaceToC.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/interface/InterfaceToC.lua
new file mode 100644
index 0000000000..282727b66a
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/interface/InterfaceToC.lua
@@ -0,0 +1,78 @@
+-- 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$
+
+
+---
+-- This table contains functions that are used (on the Lua side) to
+-- prepare a graph for use in C and, vice versa, to translate back the
+-- results of C to Lua.
+
+local InterfaceToC = {}
+
+-- Imports
+
+local lib = require "pgf.gd.lib"
+
+
+---
+-- This function is called by |declare| for ``algorithm
+-- keys'' where the algorithm is not written in Lua, but rather in the
+-- programming language C. You do not call this function yourself;
+-- |InterfaceFromC.h| will do it for you. Nevertheless, if you
+-- provide a table to |declare| with the field
+-- |algorithm_written_in_c| set, the following happens: The table's
+-- |algorithm| field is set to an algorithm class object whose |run|
+-- method calls the function passed via the
+-- |algorithm_written_in_c| field. It will be called with the
+-- following parameters (in that order):
+-- %
+-- \begin{enumerate}
+-- \item The to-be-laid out digraph. This will not be the whole layout
+-- graph (syntactic digraph) if preprocessing like decomposition into
+-- connected components is used.
+-- \item An array of the digraph's vertices, but with the table part
+-- hashing vertex objects to their indices in the array part.
+-- \item An array of the syntactic edges of the digraph. Like the
+-- array, the table part will hash back the indices of the edge objects.
+-- \item The algorithm object.
+-- \end{enumerate}
+--
+-- @param t The table originally passed to |declare|.
+
+function InterfaceToC.declare_algorithm_written_in_c (t)
+ t.algorithm = {
+ run = function (self)
+ local back_table = lib.icopy(self.ugraph.vertices)
+ for i,v in ipairs(self.ugraph.vertices) do
+ back_table[v] = i
+ end
+ local edges = {}
+ for _,a in ipairs(self.ugraph.arcs) do
+ local b = self.layout_graph:arc(a.tail,a.head)
+ if b then
+ lib.icopy(b.syntactic_edges, edges)
+ end
+ end
+ for i=1,#edges do
+ edges[edges[i]] = i
+ end
+ collectgarbage("stop") -- Remove once Lua Link Bug is fixed
+ t.algorithm_written_in_c (self.digraph, back_table, edges, self)
+ collectgarbage("restart") -- Remove once Lua Link Bug is fixed
+ end
+ }
+end
+
+
+
+-- Done
+
+return InterfaceToC
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/interface/InterfaceToDisplay.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/interface/InterfaceToDisplay.lua
new file mode 100644
index 0000000000..23018efcaa
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/interface/InterfaceToDisplay.lua
@@ -0,0 +1,1002 @@
+-- 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$
+
+
+
+---
+-- This class provides the interface between a display
+-- layer (like \tikzname\ or a graph editor) and graph drawing
+-- system. Another class, |InterfaceToAlgorithms|, binds the algorithm
+-- layer (which are written in Lua) to the graph drawing system.
+--
+-- The functions declared here are independent of the actual display
+-- layer. Rather, the differences between the layers are encapsulated
+-- by subclasses of the |Binding| class, see that class for
+-- details. Thus, when a new display layer is written, the present
+-- class is \emph{used}, but not \emph{modified}. Instead, only a new
+-- binding is created and all display layer specific interaction is
+-- put there.
+--
+-- The job of this class is to provide convenient methods that can be
+-- called by the display layer. For instance, it provides methods for
+-- starting a graph drawing scope, managing the stack of such scope,
+-- adding a node to a graph and so on.
+
+local InterfaceToDisplay = {}
+
+-- Namespace
+require("pgf.gd.interface").InterfaceToDisplay = InterfaceToDisplay
+
+
+-- Imports
+local InterfaceCore = require "pgf.gd.interface.InterfaceCore"
+local Scope = require "pgf.gd.interface.Scope"
+
+local Binding = require "pgf.gd.bindings.Binding"
+
+local Sublayouts = require "pgf.gd.control.Sublayouts"
+local LayoutPipeline = require "pgf.gd.control.LayoutPipeline"
+
+local Digraph = require "pgf.gd.model.Digraph"
+local Vertex = require "pgf.gd.model.Vertex"
+local Edge = require "pgf.gd.model.Edge"
+local Collection = require "pgf.gd.model.Collection"
+
+local Storage = require "pgf.gd.lib.Storage"
+local LookupTable = require "pgf.gd.lib.LookupTable"
+local Event = require "pgf.gd.lib.Event"
+
+local lib = require "pgf.gd.lib"
+
+
+-- Forward declarations
+local get_current_options_table
+local render_collections
+local push_on_option_stack
+local vertex_created
+
+-- Local objects
+
+local phase_unique = {} -- a unique handle
+local collections_unique = {} -- a unique handle
+local option_cache = nil -- The option cache
+
+
+
+
+---
+-- Initialize the binding. This function is called once by the display
+-- layer at the very beginning. For instance, \tikzname\ does the
+-- following call:
+-- %
+--\begin{codeexample}[code only, tikz syntax=false]
+--InterfaceToDisplay.bind(require "pgf.gd.bindings.BindingToPGF")
+--\end{codeexample}
+--
+-- Inside this call, many standard declarations will be executed, that
+-- is, the declared binding will be used immediately.
+--
+-- Subsequently, the |binding| field of the |InterfaceCore| can be used.
+--
+-- @param class A subclass of |Binding|.
+
+function InterfaceToDisplay.bind(class)
+ assert (not InterfaceCore.binding, "binding already initialized")
+
+ -- Create a new object
+ InterfaceCore.binding = setmetatable({}, class)
+
+ -- Load these libraries, which contain many standard declarations:
+ require "pgf.gd.model.library"
+ require "pgf.gd.control.library"
+end
+
+
+
+
+---
+-- Start a graph drawing scope. Note that this is not the same as
+-- starting a subgraph / sublayout, which are local to a graph drawing
+-- scope: When a new graph drawing scope is started, it is pushed on
+-- top of a stack of graph drawing scopes and all other ``open''
+-- scopes are no longer directly accessible. All method calls to an
+-- |Interface...| object will refer to this newly created scope until
+-- either a new scope is opened or until the current scope is closed
+-- once more.
+--
+-- Each graph drawing scope comes with a syntactic digraph that is
+-- build using methods like |addVertex| or |addEdge|.
+--
+-- @param height The to-be-used height of the options stack. All
+-- options above this height will be popped prior to attacking the
+-- options to the syntactic digraph.
+
+function InterfaceToDisplay.beginGraphDrawingScope(height)
+
+ -- Create a new scope table
+ local scope = Scope.new {}
+
+ -- Setup syntactic digraph:
+ local g = scope.syntactic_digraph
+
+ g.options = get_current_options_table(height)
+ g.syntactic_digraph = g
+ g.scope = scope
+
+ -- Push scope:
+ InterfaceCore.scopes[#InterfaceCore.scopes + 1] = scope
+end
+
+
+
+---
+-- Arranges the current graph using the specified algorithm and options.
+--
+-- This function should be called after the graph drawing scope has
+-- been opened and the syntactic digraph has been completely
+-- specified. It will now start running the algorithm specified
+-- through the |algorithm_phase| options.
+--
+-- Internally, this function creates a coroutine that will run the current graph
+-- drawing algorithm. Coroutines are needed since a graph drawing
+-- algorithm may choose to create a new node. In this case, the
+-- algorithm needs to be suspended and control must be returned back
+-- to the display layer, so that the node can be typeset in order to
+-- determine the precise size information. Once this is done, control
+-- must be passed back to the exact point inside the algorithm where
+-- the node was created. Clearly, all of these actions are exactly
+-- what coroutines are for.
+--
+-- @return Time it took to run the algorithm
+
+function InterfaceToDisplay.runGraphDrawingAlgorithm()
+
+ -- Time things
+ local start = os.clock()
+
+ -- Setup
+ local scope = InterfaceCore.topScope()
+ assert(not scope.coroutine, "coroutine already created for current gd scope")
+
+ -- The actual drawing function
+ local function run ()
+ if #scope.syntactic_digraph.vertices == 0 then
+ -- Nothing needs to be done
+ return
+ end
+
+ LayoutPipeline.run(scope)
+ end
+
+ scope.coroutine = coroutine.create(run)
+
+ -- Run it:
+ InterfaceToDisplay.resumeGraphDrawingCoroutine()
+
+ -- End timing:
+ local stop = os.clock()
+
+ return stop - start
+end
+
+
+---
+-- Resume the graph drawing coroutine.
+--
+-- This function is the work horse of the coroutine management. It
+-- gets called whenever control passes back from the display layer to
+-- the algorithm level. We resume the graph drawing coroutine so that the
+-- algorithm can start/proceed. The tricky part is when the algorithm
+-- yields, but is not done. In this case, the code needed for creating
+-- a new node is passed back to the display layer through the binding,
+-- which must then execute the code and then resuming the coroutine.
+--
+function InterfaceToDisplay.resumeGraphDrawingCoroutine()
+
+ -- Setup
+ local scope = InterfaceCore.topScope()
+ local binding = InterfaceCore.binding
+
+ -- Asserts
+ assert(scope.coroutine, "coroutine not created for current gd scope")
+
+ -- Run
+ local ok, text = coroutine.resume(scope.coroutine)
+ assert(ok, text)
+ if coroutine.status(scope.coroutine) ~= "dead" then
+ -- Ok, ask binding to continue
+ binding:resumeGraphDrawingCoroutine(text)
+ end
+end
+
+
+
+--- Ends the current graph drawing scope.
+--
+function InterfaceToDisplay.endGraphDrawingScope()
+ assert(#InterfaceCore.scopes > 0, "no gd scope open")
+ InterfaceCore.scopes[#InterfaceCore.scopes] = nil -- pop
+end
+
+
+
+
+---
+-- Creates a new vertex in the syntactic graph of the current graph
+-- drawing scope. The display layer should call this function for each
+-- node of the graph. The |name| must be a unique string identifying
+-- the node. The newly created vertex will be added to the syntactic
+-- digraph. The binding function |everyVertexCreation| will then be
+-- called, allowing the binding to store information regarding the newly
+-- created vertex.
+--
+-- For each vertex an event will be created in the event
+-- sequence. This event will have the kind |"node"| and its
+-- |parameter| will be the vertex.
+--
+-- @param name Name of the vertex.
+--
+-- @param shape The shape of the vertex such as |"circle"| or
+-- |"rectangle"|. This shape may help a graph drawing algorithm
+-- figuring out how the node should be placed.
+--
+-- @param path A |Path| object representing the vertex's path.
+--
+-- @param height The to-be-used height of the options stack. All
+-- options above this height will be popped prior to attacking the
+-- options to the syntactic digraph.
+--
+-- @param binding_infos These options are passed to and are specific
+-- to the current |Binding|.
+--
+-- @param anchors A table of anchors (mapping anchor positions to
+-- |Coordinates|).
+
+
+function InterfaceToDisplay.createVertex(name, shape, path, height, binding_infos, anchors)
+
+ -- Setup
+ local scope = InterfaceCore.topScope()
+ local binding = InterfaceCore.binding
+
+ -- Does vertex already exist?
+ local v = scope.node_names[name]
+ assert (not v or not v.created_on_display_layer, "node already created")
+
+ -- Create vertex
+ if not v then
+ v = Vertex.new {
+ name = name,
+ shape = shape,
+ kind = "node",
+ path = path,
+ options = get_current_options_table(height),
+ anchors = anchors,
+ }
+
+ vertex_created(v,scope)
+ else
+ assert(v.kind == "subgraph node", "subgraph node expected")
+ v.shape = shape
+ v.path = path
+ v.anchors = anchors
+ end
+
+ v.created_on_display_layer = true
+
+ -- Call binding
+ binding.storage[v] = binding_infos
+ binding:everyVertexCreation(v)
+end
+
+
+-- This is a helper function
+function vertex_created(v,scope)
+
+ -- Create Event
+ local e = InterfaceToDisplay.createEvent ("node", v)
+ v.event = e
+
+ -- Create name lookup
+ scope.node_names[v.name] = v
+
+ -- Add vertex to graph
+ scope.syntactic_digraph:add {v}
+
+ -- Add to collections
+ for _,c in ipairs(v.options.collections) do
+ LookupTable.addOne(c.vertices, v)
+ end
+
+end
+
+
+
+---
+-- Creates a new vertex in the syntactic graph of the current graph
+-- drawing scope that is a subgraph vertex. Such a vertex
+-- ``surrounds'' the vertices of a subgraph. The special property of a
+-- subgraph node opposed to a normal node is that it is created only
+-- after the subgraph has been laid out. However, the difference to a
+-- collection like |hyper| is that the node is available immediately as
+-- a normal node in the sense that you can connect edges to it.
+--
+-- What happens internally is that subgraph nodes get ``registered''
+-- immediately both on the display level and on the algorithm level,
+-- but the actual node is only created inside the layout pipeline
+-- using a callback of the binding. The present function is used to
+-- perform this registering. The node creation happens when the
+-- innermost layout in which the subgraph node is declared has
+-- finished. For each subgraph node, a collection is created that
+-- contains all vertices (and edges) being part of the subgraph. For
+-- this reason, this method is a |push...| method, since it pushes
+-- something on the options stack.
+--
+-- The |init| parameter will be used during the creation of the node,
+-- see |Binding:createVertex| for details on the fields. Note that
+-- |init.text| is often not displayed for such ``vast'' nodes as those
+-- created for whole subgraphs, but a shape may use it nevertheless
+-- (for instance, one might display this text at the top of the node
+-- or, in case of a \textsc{uml} package, in a special box above the
+-- actual node).
+--
+-- The |init.generated_options| will be augmented by additional
+-- key--value pairs when the vertex is created:
+-- %
+-- \begin{itemize}
+-- \item The key |subgraph point cloud| will have as its value a
+-- string that is be a list of points (without separating commas)
+-- like |"(10pt,20pt)(0pt,0pt)(30pt,40pt)"|, always in
+-- this syntax. The list will contain all points inside the
+-- subgraph. In particular, a bounding box around these points will
+-- encompass all nodes and bend points of the subgraph.
+-- The bounding box of this point cloud is guaranteed to be centered on
+-- the origin.
+-- \item The key |subgraph bounding box width| will have as its value
+-- the width of a bounding box (in \TeX\ points, as a string with the
+-- suffix |"pt"|).
+-- \item The key |subgraph bounding box height| stores the height of a
+-- bounding box.
+-- \end{itemize}
+--
+-- @param name The name of the node.
+-- @param height Height of the options stack. Note that this method
+-- pushes something (namely a collection) on the options stack.
+-- @param info A table passed to |Binding:createVertex|, see that function.
+--
+function InterfaceToDisplay.pushSubgraphVertex(name, height, info)
+
+ -- Setup
+ local scope = InterfaceCore.topScope()
+ local binding = InterfaceCore.binding
+
+ -- Does vertex already exist?
+ assert (not scope.node_names[name], "node already created")
+
+ -- Create vertex
+ local v = Vertex.new {
+ name = name,
+ kind = "subgraph node",
+ options = get_current_options_table(height-1)
+ }
+
+ vertex_created(v,scope)
+
+ -- Store info
+ info.generated_options = info.generated_options or {}
+ info.name = name
+ v.subgraph_info = info
+
+ -- Create collection and link it to v
+ local _, _, entry = InterfaceToDisplay.pushOption(InterfaceCore.subgraph_node_kind, nil, height)
+ v.subgraph_collection = entry.value
+ v.subgraph_collection.subgraph_node = v
+
+ -- Find parent collection in options stack:
+ local collections = v.options.collections
+ for i=#collections,1,-1 do
+ if collections[i].kind == InterfaceCore.sublayout_kind then
+ v.subgraph_collection.parent_layout = collections[i]
+ break
+ end
+ end
+end
+
+
+
+---
+-- Add options for an already existing vertex.
+--
+-- This function allows you to add options to an already existing
+-- vertex. The options that will be added are all options on the
+-- current options stack; they will overwrite existing options of the
+-- same name. For collections, the vertex stays in all collections it
+-- used to, it is only added to all collections that are currently on
+-- the options stack.
+--
+-- @param name Name of the vertex.
+-- @param height The option stack height.
+
+function InterfaceToDisplay.addToVertexOptions(name, height)
+
+ -- Setup
+ local scope = InterfaceCore.topScope()
+
+ -- Does vertex already exist?
+ local v = assert (scope.node_names[name], "node is missing, cannot add options")
+
+ v.options = get_current_options_table(height, v.options)
+
+ -- Add to collections
+ for _,c in ipairs(v.options.collections) do
+ LookupTable.addOne(c.vertices, v)
+ end
+
+end
+
+
+
+
+
+---
+-- Creates a new edge in the syntactic graph of the current graph
+-- drawing scope. The display layer should call this function for each
+-- edge that is created. Both the |from| vertex and the |to| vertex
+-- must exist (have been created through |createVertex|) prior to your
+-- being able to call this function.
+--
+-- After the edge has been created, the binding layer's function
+-- |everyEdgeCreation| will be called, allowing the binding layer to
+-- store information about the edge.
+--
+-- For each edge an event is created, whose kind is |"edge"| and whose
+-- |parameter| is a two-element array whose first entry is the edge's
+-- arc in the syntactic digraph and whose second entry is the position
+-- of the edge in the arc's array of syntactic edges.
+--
+-- @param tail Name of the node the edge begins at.
+-- @param head Name of the node the edge ends at.
+-- @param direction Direction of the edge (e.g. |--| for an undirected edge
+-- or |->| for a directed edge from the first to the second
+-- node).
+-- @param height The option stack height, see for instance |createVertex|.
+--
+-- @param binding_infos These options will be stored in the |storage|
+-- of the vertex at the field index by the binding.
+
+function InterfaceToDisplay.createEdge(tail, head, direction, height, binding_infos)
+
+ -- Setup
+ local scope = InterfaceCore.topScope()
+ local binding = InterfaceCore.binding
+
+ -- Does vertex already exist?
+ local h = scope.node_names[head]
+ local t = scope.node_names[tail]
+ assert (h and t, "attempting to create edge between nodes that are not in the graph")
+
+ -- Create Arc object
+ local arc = scope.syntactic_digraph:connect(t, h)
+
+ -- Create Edge object
+ local edge = Edge.new {
+ head = h,
+ tail = t,
+ direction = direction,
+ options = get_current_options_table(height)
+ }
+
+ -- Add to arc
+ arc.syntactic_edges[#arc.syntactic_edges+1] = edge
+
+ -- Create Event
+ local e = InterfaceToDisplay.createEvent ("edge", { arc, #arc.syntactic_edges })
+ edge.event = e
+
+ -- Make part of collections
+ for _,c in ipairs(edge.options.collections) do
+ LookupTable.addOne(c.edges, edge)
+ end
+
+ -- Call binding
+ binding.storage[edge] = binding_infos
+ binding:everyEdgeCreation(edge)
+
+end
+
+
+
+
+
+---
+-- Push an option to the stack of options.
+--
+-- As a graph is parsed, a stack of ``current options''
+-- is created. To add something to this table, the display layers may
+-- call the method |pushOption|. To pop something from this stack,
+-- just set the |height| value during the next push to the position to
+-- which you actually wish to push something; everything above and
+-- including this position will be popped from the stack.
+--
+-- When an option is pushed, several additional options may also be
+-- pushed, namely whenever the option has a |use| field set. These
+-- additional options may, in turn, also push new options. Because of
+-- this, this function returns a new stack height, representing the
+-- resulting stack height.
+--
+-- In addition to this stack height, this function returns a Boolean
+-- value indicating whether a ``main algorithm phase was set''. This
+-- happens whenever a key is executed (directly or indirectly through
+-- the |use| field) that selects an algorithm for the ``main''
+-- algorithm phase. This information may help the caller to setup the
+-- graph drawing scopes correctly.
+--
+-- @param key A parameter (must be a string).
+-- @param value A value (can be anything). If it is a string, it will
+-- be converted to whatever the key expects.
+-- @param height A stack height at which to insert the key. Everything
+-- above this height will be removed.
+--
+-- @return A new stack height
+-- @return A Boolean that is |true| if the main algorithm phase was
+-- set by the option or one option |use|d by it.
+-- @return The newly created entry on the stack. If more entries are
+-- created through the use of the |use| field, the original entry is
+-- returned nevertheless.
+
+
+function InterfaceToDisplay.pushOption(key, value, height)
+ assert(type(key) == "string", "illegal key")
+
+ local key_record = assert(InterfaceCore.keys[key], "unknown key")
+ local main_phase_set = false
+
+ if value == nil and key_record.default then
+ value = key_record.default
+ end
+
+ -- Find out what kind of key we are pushing:
+
+ if key_record.algorithm then
+ -- Push a phase
+ if type(InterfaceCore.algorithm_classes[key]) == "function" then
+ -- Call the constructor function
+ InterfaceCore.algorithm_classes[key] = InterfaceCore.algorithm_classes[key]()
+ end
+
+ local algorithm = InterfaceCore.algorithm_classes[key]
+
+ assert (algorithm, "algorithm class not found")
+
+ push_on_option_stack(phase_unique,
+ { phase = value or key_record.phase, algorithm = algorithm },
+ height)
+
+ if key_record.phase == "main" then
+ main_phase_set = true
+ end
+
+ elseif key_record.layer then
+ -- Push a collection
+ local stack = InterfaceCore.option_stack
+ local scope = InterfaceCore.topScope()
+
+ -- Get the stack above "height":
+ local options = get_current_options_table(height-1)
+
+ -- Create the collection event
+ local event = InterfaceToDisplay.createEvent ("collection", key)
+
+ -- Create collection object:
+ local collection = Collection.new { kind = key, options = options, event = event }
+
+ -- Store in collections table of current scope:
+ local collections = scope.collections[key] or {}
+ collections[#collections + 1] = collection
+ scope.collections[key] = collections
+
+ -- Build collection tree
+ collection:registerAsChildOf(options.collections[#options.collections])
+
+ -- Push on stack
+ push_on_option_stack(collections_unique, collection, height)
+
+ else
+
+ -- A normal key
+ push_on_option_stack(key, InterfaceCore.convert(value, InterfaceCore.keys[key].type), height)
+
+ end
+
+ local newly_created = InterfaceCore.option_stack[#InterfaceCore.option_stack]
+
+ -- Now, push use keys:
+ local use = key_record.use
+ if key_record.use then
+ local flag
+ for _,u in ipairs(InterfaceCore.keys[key].use) do
+ local use_k = u.key
+ local use_v = u.value
+ if type(use_k) == "function" then
+ use_k = use_k(value)
+ end
+ if type(use_v) == "function" then
+ use_v = use_v(value)
+ end
+ height, flag = InterfaceToDisplay.pushOption(use_k, use_v, height+1)
+ main_phase_set = main_phase_set or flag
+ end
+ end
+
+ return height, main_phase_set, newly_created
+end
+
+
+---
+-- Push a layout on the stack of options. As long as this layout is on
+-- the stack, all vertices and edges will be part of this layout. For
+-- details on layouts, please see |Sublayouts|.
+--
+-- @param height A stack height at which to insert the key. Everything
+-- above this height will be removed.
+
+function InterfaceToDisplay.pushLayout(height)
+ InterfaceToDisplay.pushOption(InterfaceCore.sublayout_kind, nil, height)
+end
+
+
+
+---
+-- Creates an event and adds it to the event string of the current scope.
+--
+-- @param kind Name/kind of the event.
+-- @param parameters Parameters of the event.
+--
+-- @return The newly pushed event
+--
+function InterfaceToDisplay.createEvent(kind, param)
+ local scope = InterfaceCore.topScope()
+ local n = #scope.events + 1
+ local e = Event.new { kind = kind, parameters = param, index = n }
+ scope.events[n] = e
+
+ return e
+end
+
+
+
+---
+-- This method allows you to query the table of all declared keys. It
+-- contains them both as an array and also as a table index by the
+-- keys's names. In particular, you can then iterate over it using
+-- |ipairs| and you can check whether a key is defined by accessing
+-- the table at the key's name. Each entry of the table is the
+-- original table passed to |InterfaceToAlgorithms.declare|.
+--
+-- @return A lookup table of all declared keys.
+
+function InterfaceToDisplay.getDeclaredKeys()
+ return InterfaceCore.keys
+end
+
+
+
+
+---
+-- Renders the graph.
+--
+-- This function is called after the graph has been laid out by the
+-- graph drawing algorithms. It will trigger a sequence of calls to
+-- the binding layer that will, via callbacks, start rendering the
+-- whole graph.
+--
+-- In detail, this function calls:
+-- %
+--\begin{codeexample}[code only, tikz syntax=false]
+--local binding = InterfaceCore.binding
+--
+--binding:renderStart()
+--render_vertices()
+--render_edges()
+--render_collections()
+--binding:renderStop()
+--\end{codeexample}
+--
+-- Here, the |render_...| functions are local, internal functions that are,
+-- nevertheless, documented here.
+--
+-- @param name Returns the algorithm class that has been declared using
+-- |declare| under the given name.
+
+function InterfaceToDisplay.renderGraph()
+ local scope = InterfaceCore.topScope()
+ local syntactic_digraph = scope.syntactic_digraph
+
+ local binding = InterfaceCore.binding
+
+ binding:renderStart()
+ render_vertices(syntactic_digraph.vertices)
+ render_edges(syntactic_digraph.arcs)
+ render_collections(scope.collections)
+ binding:renderStop()
+end
+
+
+
+
+
+---
+-- Render the vertices after the graph drawing algorithm has
+-- finished. This function is local and internal and included only for
+-- documenting the call graph.
+--
+-- When the graph drawing algorithm is done, the interface will start
+-- rendering the vertices by calling appropriate callbacks of the
+-- binding layer.
+--
+-- Consider the following code:
+-- %
+--\begin{codeexample}[code only]
+--\graph [... layout] {
+-- a -- b -- c -- d;
+--};
+--\end{codeexample}
+--
+-- In this case, after the graph drawing algorithm has run, the
+-- present function will call:
+-- %
+--\begin{codeexample}[code only, tikz syntax=false]
+--local binding = InterfaceCore.binding
+--
+--binding:renderVerticesStart()
+--binding:renderVertex(vertex_a)
+--binding:renderVertex(vertex_b)
+--binding:renderVertex(vertex_c)
+--binding:renderVertex(vertex_d)
+--binding:renderVerticesStop()
+--\end{codeexample}
+--
+-- @param vertices An array of all vertices in the syntactic digraph.
+
+function render_vertices(vertices)
+ InterfaceCore.binding:renderVerticesStart()
+ for _,vertex in ipairs(vertices) do
+ InterfaceCore.binding:renderVertex(vertex)
+ end
+ InterfaceCore.binding:renderVerticesStop()
+end
+
+
+---
+-- Render the collections whose layer is not |0|. This local, internal
+-- function is called to render the different collection kinds.
+--
+-- Collection kinds rendered in the order provided by the |layer|
+-- field passed to |declare| during the declaration of the collection
+-- kind, see also |declare_collection|. If several collection kinds
+-- have the same layer, they are rendered in lexicographical ordering
+-- (to ensure that they are always rendered in the same order).
+--
+-- Consider the following code:
+-- %
+--\begin{codeexample}[code only, tikz syntax=false]
+--declare { key = "hyper", layer = 1 }
+--\end{codeexample}
+-- you can say on the \tikzname\ layer
+--\begin{codeexample}[code only]
+--\graph {
+-- a, b, c, d;
+-- { [hyper] a, b, c }
+-- { [hyper] b, c, d }
+--};
+--\end{codeexample}
+--
+-- In this case, after the graph drawing algorithm has run, the
+-- present function will call:
+--
+--\begin{codeexample}[code only, tikz syntax=false]
+--local binding = InterfaceCore.binding
+--
+--binding:renderCollectionStartKind("hyper", 1)
+--binding:renderCollection(collection_containing_abc)
+--binding:renderCollection(collection_containing_bcd)
+--binding:renderCollectionStopKind("hyper", 1)
+--\end{codeexample}
+--
+-- @param collections The |collections| table of the current scope.
+
+function render_collections(collections)
+ local kinds = InterfaceCore.collection_kinds
+ local binding = InterfaceCore.binding
+
+ for i=1,#kinds do
+ local kind = kinds[i].kind
+ local layer = kinds[i].layer
+
+ if layer ~= 0 then
+ binding:renderCollectionStartKind(kind, layer)
+ for _,c in ipairs(collections[kind] or {}) do
+ binding:renderCollection(c)
+ end
+ binding:renderCollectionStopKind(kind, layer)
+ end
+ end
+end
+
+
+---
+-- Render the syntactic edges of a graph after the graph drawing
+-- algorithm has finished. This function is local and internal and included only
+-- for documenting the call graph.
+--
+-- When the graph drawing algorithm is done, the interface will first
+-- rendering the vertices using |render_vertices|, followed by calling
+-- this function, which in turn calls appropriate callbacks to the
+-- binding layer.
+--
+-- Consider the following code:
+-- %
+--\begin{codeexample}[code only]
+-- \graph [... layout] {
+-- a -- b -- c -- d;
+-- };
+--\end{codeexample}
+--
+-- In this case, after the graph drawing algorithm has run, the
+-- present function will call:
+-- %
+--\begin{codeexample}[code only, tikz syntax=false]
+-- local binding = InterfaceCore.binding
+--
+-- binding:renderEdgesStart()
+-- binding:renderEdge(edge_from_a_to_b)
+-- binding:renderEdge(edge_from_b_to_c)
+-- binding:renderEdge(edge_from_c_to_d)
+-- binding:renderEdgesStop()
+--\end{codeexample}
+--
+-- @param arcs The array of arcs of the syntactic digraph.
+
+function render_edges(arcs)
+ InterfaceCore.binding:renderEdgesStart()
+ for _,a in ipairs(arcs) do
+ for _,e in ipairs (a.syntactic_edges) do
+ InterfaceCore.binding:renderEdge(e)
+ end
+ end
+ InterfaceCore.binding:renderEdgesStop()
+end
+
+
+local aliases = InterfaceCore.option_aliases
+local option_initial = InterfaceCore.option_initial
+
+local option_metatable = {
+ __index =
+ function (t, key)
+ local k = aliases[key]
+ if k then
+ local v = (type(k) == "string" and t[k]) or (type(k) == "function" and k(t)) or nil
+ if v ~= nil then
+ return v
+ end
+ end
+ return option_initial[key]
+ end
+}
+
+
+---
+-- Get the current options table.
+--
+-- An option table can be accessed like a normal table; however, there
+-- is a global fallback for this table. If an index is not defined,
+-- the value of this index in the global fallback table is used. (This
+-- reduces the overall amount of option keys that need to be stored
+-- with object.)
+--
+-- (This function is local and internal and included only for documentation
+-- purposes.)
+--
+-- @param height The stack height for which the option table is
+-- required.
+-- @param table If non |nil|, the options will be added to this
+-- table.
+--
+-- @return The option table as described above.
+
+function get_current_options_table (height, table)
+ local stack = InterfaceCore.option_stack
+ assert (height >= 0 and height <= #stack, "height value out of bounds")
+
+ if height == InterfaceCore.option_cache_height and not table then
+ return option_cache
+ else
+ -- Clear superfluous part of stack
+ for i=#stack,height+1,-1 do
+ stack[i] = nil
+ end
+
+ -- Build options table
+ local cache
+ if not table then
+ cache = setmetatable(
+ {
+ algorithm_phases = setmetatable({}, InterfaceCore.option_initial.algorithm_phases),
+ collections = {}
+ }, option_metatable)
+ else
+ cache = lib.copy(table)
+ cache.algorithm_phases = lib.copy(cache.algorithm_phases)
+ cache.collections = lib.copy(cache.collections)
+ end
+
+ local algorithm_phases = cache.algorithm_phases
+ local collections = cache.collections
+ local keys = InterfaceCore.keys
+
+ 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
+ cache[k] = v
+ end
+ end
+
+ for _,s in ipairs(stack) do
+ handle (s.key, s.value)
+ end
+
+ -- Cache it, if this was not added:
+ if not table then
+ InterfaceCore.option_cache_height = height
+ option_cache = cache
+ end
+
+ return cache
+ end
+end
+
+
+
+-- A helper function
+
+function push_on_option_stack(key, value, height)
+ local stack = InterfaceCore.option_stack
+
+ assert (type(height) == "number" and height > 0 and height <= #stack + 1,
+ "height value out of bounds")
+
+ -- Clear superfluous part of stack
+ for i=#stack,height+1,-1 do
+ stack[i] = nil
+ end
+
+ stack[height] = { key = key, value = value }
+ InterfaceCore.option_cache_height = nil -- invalidate cache
+end
+
+
+
+-- Done
+
+return InterfaceToDisplay
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/interface/Scope.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/interface/Scope.lua
new file mode 100644
index 0000000000..a750308e69
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/interface/Scope.lua
@@ -0,0 +1,92 @@
+-- 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$
+
+
+
+---
+-- In theory, graph drawing algorithms take graphs as input and
+-- output graphs embedded into the plane as output. In practice, however,
+-- the input to a graph drawing algorithm is not ``just'' the
+-- graph. Rather, additional information about the graph, in particular
+-- about the way the user specified the graph, is also important to many
+-- graph drawing algorithms.
+--
+-- The graph drawing system gathers both the original input graph as well
+-- as all additional information that is provided in the graph drawing
+-- scope inside a scope table. The object has a number of fields that
+-- inform an algorithm about the input.
+--
+-- For each graph drawing scope, a new |Scope| object is
+-- created. Graph drawing scopes are kept track of using a stack, but
+-- only the top of this stack is available to the interface classes.
+--
+-- @field syntactic_digraph The syntactic digraph is a digraph that
+-- faithfully encodes the way the input graph is represented
+-- syntactically. However, this does not mean that the syntactic
+-- digraph contains the actual textual representation of the input
+-- graph. Rather, when an edge is specified as, say, |a <- b|, the
+-- syntactic digraph will contains an arc from |a| to |b| with an edge
+-- object attached to it that is labeled as a ``backward''
+-- edge. Similarly, an edge |a -- b| is also stored as a directed arc
+-- from |a| to |b| with the label |--| attached to it. Algorithms will
+-- often be more interested graphs derived from the syntactic digraph
+-- such as its underlying undirected graph. These derived graphs are
+-- made accessible by the graph drawing engine during the preprocessing.
+--
+-- @field events An array of |Event| objects. These objects, see the
+-- |Event| class for details, are created during the parsing of the
+-- input graph.
+--
+-- @field node_names A table that maps the names of nodes to node
+-- objects. Every node must have a unique name.
+--
+-- @field coroutine A Lua coroutine that is used internally to allow
+-- callbacks to the display layer to be issued deep down during a run
+-- of an algorithm.
+--
+-- @field collections The collections specified inside the scope, see
+-- the |Collection| class.
+
+local Scope = {}
+Scope.__index = Scope
+
+-- Namespace
+require("pgf.gd.interface").Scope = Scope
+
+-- Imports
+local lib = require "pgf.gd.lib"
+local Storage = require "pgf.gd.lib.Storage"
+
+local Digraph = require "pgf.gd.model.Digraph"
+
+---
+-- Create a new |Scope| object.
+--
+-- @param initial A table of initial values for the newly created
+-- |Scope| object.
+--
+-- @return The new scope object.
+
+function Scope.new(initial)
+ return setmetatable(lib.copy(initial,
+ {
+ syntactic_digraph = Digraph.new{},
+ events = {},
+ node_names = {},
+ coroutine = nil,
+ collections = {},
+ }), Scope)
+end
+
+
+-- Done
+
+return Scope
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/layered.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/layered.lua
new file mode 100644
index 0000000000..6988e7a3d9
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/layered.lua
@@ -0,0 +1,159 @@
+-- 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$
+
+
+local layered = {}
+
+-- Namespace
+
+require("pgf.gd").layered = layered
+
+
+local lib = require "pgf.gd.lib"
+local Storage = require "pgf.gd.lib.Storage"
+
+--
+-- This file defines some basic functions to compute and/or set the
+-- ideal distances between nodes of any kind of layered drawing of a
+-- graph.
+
+
+---
+-- Compute the ideal distance between two siblings
+--
+-- @param paddings A |Storage| object in which the computed distances
+-- (paddings) are stored.
+-- @param graph The graph object
+-- @param n1 The first node
+-- @param n2 The second node
+
+function layered.ideal_sibling_distance (paddings, graph, n1, n2)
+ local ideal_distance
+ local sep
+
+ local n1_is_node = n1.kind == "node"
+ local n2_is_node = n2.kind == "node"
+
+ if not n1_is_node and not n2_is_node then
+ ideal_distance = graph.options['sibling distance']
+ sep = graph.options['sibling post sep']
+ + graph.options['sibling pre sep']
+ else
+ if n1_is_node then
+ ideal_distance = lib.lookup_option('sibling distance', n1, graph)
+ else
+ ideal_distance = lib.lookup_option('sibling distance', n2, graph)
+ end
+ sep = (n1_is_node and lib.lookup_option('sibling post sep', n1, graph) or 0)
+ + (n2_is_node and lib.lookup_option('sibling pre sep', n2, graph) or 0)
+ end
+
+ return math.max(ideal_distance, sep +
+ ((n1_is_node and paddings[n1].sibling_post) or 0) -
+ ((n2_is_node and paddings[n2].sibling_pre) or 0))
+end
+
+
+
+---
+-- Compute the baseline distance between two layers
+--
+-- The "baseline" distance is the distance between two layers that
+-- corresponds to the distance of the two layers if the nodes where
+-- "words" on two adjacent lines. In this case, the distance is
+-- normally the layer_distance, but will be increased such that if we
+-- draw a horizontal line below the deepest character on the first
+-- line and a horizontal line above the highest character on the
+-- second line, the lines will have a minimum distance of layer sep.
+--
+-- Since each node on the lines might have a different layer sep and
+-- layer distance specified, the maximum over all the values is taken.
+--
+-- @param paddings A |Storage| object in which the distances
+-- (paddings) are stored.
+-- @param graph The graph in which the nodes reside
+-- @param l1 An array of the nodes of the first layer
+-- @param l2 An array of the nodes of the second layer
+
+function layered.baseline_distance (paddings, graph, l1, l2)
+
+ if #l1 == 0 or #l2 == 0 then
+ return 0
+ end
+
+ local layer_distance = -math.huge
+ local layer_pre_sep = -math.huge
+ local layer_post_sep = -math.huge
+
+ local max_post = -math.huge
+ local min_pre = math.huge
+
+ for _,n in ipairs(l1) do
+ layer_distance = math.max(layer_distance, lib.lookup_option('level distance', n, graph))
+ layer_post_sep = math.max(layer_post_sep, lib.lookup_option('level post sep', n, graph))
+ if n.kind == "node" then
+ max_post = math.max(max_post, paddings[n].layer_post)
+ end
+ end
+
+ for _,n in ipairs(l2) do
+ layer_pre_sep = math.max(layer_pre_sep, lib.lookup_option('level pre sep', n, graph))
+ if n.kind == "node" then
+ min_pre = math.min(min_pre, paddings[n].layer_pre)
+ end
+ end
+
+ return math.max(layer_distance, layer_post_sep + layer_pre_sep + max_post - min_pre)
+end
+
+
+
+---
+-- Position nodes in layers using baselines
+--
+-- @param layers A |Storage| object assigning layers to vertices.
+-- @param paddings A |Storage| object storing the computed distances
+-- (paddings).
+-- @param graph The graph in which the nodes reside
+
+function layered.arrange_layers_by_baselines (layers, paddings, graph)
+
+ local layer_vertices = Storage.newTableStorage()
+
+ -- Decompose into layers:
+ for _,v in ipairs(graph.vertices) do
+ table.insert(layer_vertices[layers[v]], v)
+ end
+
+ if #layer_vertices > 0 then -- sanity check
+ -- Now compute ideal distances and store
+ local height = 0
+
+ for _,v in ipairs(layer_vertices[1]) do
+ v.pos.y = 0
+ end
+
+ for i=2,#layer_vertices do
+ height = height + layered.baseline_distance(paddings, graph, layer_vertices[i-1], layer_vertices[i])
+
+ for _,v in ipairs(layer_vertices[i]) do
+ v.pos.y = height
+ end
+ end
+ end
+end
+
+
+
+
+-- Done
+
+return layered
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/layered/CrossingMinimizationGansnerKNV1993.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/layered/CrossingMinimizationGansnerKNV1993.lua
new file mode 100644
index 0000000000..da9ff9698c
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/layered/CrossingMinimizationGansnerKNV1993.lua
@@ -0,0 +1,342 @@
+-- Copyright 2011 by Jannis Pohlmann
+--
+-- 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
+
+-- @release $Header$
+
+
+
+local CrossingMinimizationGansnerKNV1993 = {}
+
+
+-- Imports
+
+local lib = require "pgf.gd.lib"
+local DepthFirstSearch = require "pgf.gd.lib.DepthFirstSearch"
+
+
+
+function CrossingMinimizationGansnerKNV1993:run()
+
+ self:computeInitialRankOrdering()
+
+ local best_ranking = self.ranking:copy()
+ local best_crossings = self:countRankCrossings(best_ranking)
+
+ for iteration=1,24 do
+ local direction = (iteration % 2 == 0) and 'down' or 'up'
+
+ self:orderByWeightedMedian(direction)
+ self:transpose(direction)
+
+ local current_crossings = self:countRankCrossings(self.ranking)
+
+ if current_crossings < best_crossings then
+ best_ranking = self.ranking:copy()
+ best_crossings = current_crossings
+ end
+ end
+
+ self.ranking = best_ranking:copy()
+
+ return self.ranking
+end
+
+
+
+function CrossingMinimizationGansnerKNV1993:computeInitialRankOrdering()
+
+ local best_ranking = self.ranking:copy()
+ local best_crossings = self:countRankCrossings(best_ranking)
+
+ for _,direction in ipairs({'down', 'up'}) do
+
+ local function init(search)
+ for i=#self.graph.nodes,1,-1 do
+ local node = self.graph.nodes[i]
+ if direction == 'down' then
+ if node:getInDegree() == 0 then
+ search:push(node)
+ search:setDiscovered(node)
+ end
+ else
+ if node:getOutDegree() == 0 then
+ search:push(node)
+ search:setDiscovered(node)
+ end
+ end
+ end
+ end
+
+ local function visit(search, node)
+ search:setVisited(node, true)
+
+ local rank = self.ranking:getRank(node)
+ local pos = self.ranking:getRankSize(rank)
+ self.ranking:setRankPosition(node, pos)
+
+ if direction == 'down' then
+ local out = node:getOutgoingEdges()
+ for i=#out,1,-1 do
+ local neighbour = out[i]:getNeighbour(node)
+ if not search:getDiscovered(neighbour) then
+ search:push(neighbour)
+ search:setDiscovered(neighbour)
+ end
+ end
+ else
+ local into = node:getIncomingEdges()
+ for i=#into,1,-1 do
+ local neighbour = into[i]:getNeighbour(node)
+ if not search:getDiscovered(neighbour) then
+ search:push(neighbour)
+ search:setDiscovered(neighbour)
+ end
+ end
+ end
+ end
+
+ DepthFirstSearch.new(init, visit):run()
+
+ local crossings = self:countRankCrossings(self.ranking)
+
+ if crossings < best_crossings then
+ best_ranking = self.ranking:copy()
+ best_crossings = crossings
+ end
+ end
+
+ self.ranking = best_ranking:copy()
+
+end
+
+
+
+function CrossingMinimizationGansnerKNV1993:countRankCrossings(ranking)
+
+ local crossings = 0
+
+ local ranks = ranking:getRanks()
+
+ for rank_index = 2, #ranks do
+ local nodes = ranking:getNodes(ranks[rank_index])
+ for i = 1, #nodes-1 do
+ for j = i+1, #nodes do
+ local v = nodes[i]
+ local w = nodes[j]
+
+ -- TODO Jannis: We are REQUIRED to only check edges that lead to nodes
+ -- on the next or previous rank, depending on the sweep direction!!!!
+ local cn_vw = self:countNodeCrossings(ranking, v, w, 'down')
+
+ crossings = crossings + cn_vw
+ end
+ end
+ end
+
+ return crossings
+end
+
+
+
+function CrossingMinimizationGansnerKNV1993:countNodeCrossings(ranking, left_node, right_node, sweep_direction)
+
+ local ranks = ranking:getRanks()
+ local _, rank_index = lib.find(ranks, function (rank)
+ return rank == ranking:getRank(left_node)
+ end)
+ local other_rank_index = (sweep_direction == 'down') and rank_index-1 or rank_index+1
+
+ assert(ranking:getRank(left_node) == ranking:getRank(right_node))
+ assert(rank_index >= 1 and rank_index <= #ranks)
+
+ -- 0 crossings if we're at the top or bottom and are sweeping down or up
+ if other_rank_index < 1 or other_rank_index > #ranks then
+ return 0
+ end
+
+ local left_edges = {}
+ local right_edges = {}
+
+ if sweep_direction == 'down' then
+ left_edges = left_node:getIncomingEdges()
+ right_edges = right_node:getIncomingEdges()
+ else
+ left_edges = left_node:getOutgoingEdges()
+ right_edges = right_node:getOutgoingEdges()
+ end
+
+ local crossings = 0
+
+ local function left_neighbour_on_other_rank(edge)
+ local neighbour = edge:getNeighbour(left_node)
+ return ranking:getRank(neighbour) == ranking:getRanks()[other_rank_index]
+ end
+
+ local function right_neighbour_on_other_rank(edge)
+ local neighbour = edge:getNeighbour(right_node)
+ return ranking:getRank(neighbour) == ranking:getRanks()[other_rank_index]
+ end
+
+ for _,left_edge in ipairs(left_edges) do
+ if left_neighbour_on_other_rank(left_edge) then
+ local left_neighbour = left_edge:getNeighbour(left_node)
+
+ for _,right_edge in ipairs(right_edges) do
+ if right_neighbour_on_other_rank(right_edge) then
+ local right_neighbour = right_edge:getNeighbour(right_node)
+
+ local left_position = ranking:getRankPosition(left_neighbour)
+ local right_position = ranking:getRankPosition(right_neighbour)
+
+ local neighbour_diff = right_position - left_position
+
+ if neighbour_diff < 0 then
+ crossings = crossings + 1
+ end
+ end
+ end
+ end
+ end
+
+ return crossings
+end
+
+
+
+function CrossingMinimizationGansnerKNV1993:orderByWeightedMedian(direction)
+
+ local median = {}
+
+ local function get_index(n, node) return median[node] end
+ local function is_fixed(n, node) return median[node] < 0 end
+
+ if direction == 'down' then
+ local ranks = self.ranking:getRanks()
+
+ for rank_index = 2, #ranks do
+ median = {}
+ local nodes = self.ranking:getNodes(ranks[rank_index])
+ for _,node in ipairs(nodes) do
+ median[node] = self:computeMedianPosition(node, ranks[rank_index-1])
+ end
+
+ self.ranking:reorderRank(ranks[rank_index], get_index, is_fixed)
+ end
+ else
+ local ranks = self.ranking:getRanks()
+
+ for rank_index = 1, #ranks-1 do
+ median = {}
+ local nodes = self.ranking:getNodes(ranks[rank_index])
+ for _,node in ipairs(nodes) do
+ median[node] = self:computeMedianPosition(node, ranks[rank_index+1])
+ end
+
+ self.ranking:reorderRank(ranks[rank_index], get_index, is_fixed)
+ end
+ end
+end
+
+
+
+function CrossingMinimizationGansnerKNV1993:computeMedianPosition(node, prev_rank)
+
+ local positions = lib.imap(
+ node.edges,
+ function (edge)
+ local n = edge:getNeighbour(node)
+ if self.ranking:getRank(n) == prev_rank then
+ return self.ranking:getRankPosition(n)
+ end
+ end)
+
+ table.sort(positions)
+
+ local median = math.ceil(#positions / 2)
+ local position = -1
+
+ if #positions > 0 then
+ if #positions % 2 == 1 then
+ position = positions[median]
+ elseif #positions == 2 then
+ return (positions[1] + positions[2]) / 2
+ else
+ local left = positions[median-1] - positions[1]
+ local right = positions[#positions] - positions[median]
+ position = (positions[median-1] * right + positions[median] * left) / (left + right)
+ end
+ end
+
+ return position
+end
+
+
+
+function CrossingMinimizationGansnerKNV1993:transpose(sweep_direction)
+
+ local function transpose_rank(rank)
+
+ local improved = false
+
+ local nodes = self.ranking:getNodes(rank)
+
+ for i = 1, #nodes-1 do
+ local v = nodes[i]
+ local w = nodes[i+1]
+
+ local cn_vw = self:countNodeCrossings(self.ranking, v, w, sweep_direction)
+ local cn_wv = self:countNodeCrossings(self.ranking, w, v, sweep_direction)
+
+ if cn_vw > cn_wv then
+ improved = true
+
+ self:switchNodePositions(v, w)
+ end
+ end
+
+ return improved
+ end
+
+ local ranks = self.ranking:getRanks()
+
+ local improved = false
+ repeat
+ local improved = false
+
+ if sweep_direction == 'down' then
+ for rank_index = 1, #ranks-1 do
+ improved = transpose_rank(ranks[rank_index]) or improved
+ end
+ else
+ for rank_index = #ranks-1, 1, -1 do
+ improved = transpose_rank(ranks[rank_index]) or improved
+ end
+ end
+ until not improved
+end
+
+
+
+function CrossingMinimizationGansnerKNV1993:switchNodePositions(left_node, right_node)
+ assert(self.ranking:getRank(left_node) == self.ranking:getRank(right_node))
+ assert(self.ranking:getRankPosition(left_node) < self.ranking:getRankPosition(right_node))
+
+ local left_position = self.ranking:getRankPosition(left_node)
+ local right_position = self.ranking:getRankPosition(right_node)
+
+ self.ranking:switchPositions(left_node, right_node)
+
+ local nodes = self.ranking:getNodes(self.ranking:getRank(left_node))
+end
+
+
+
+-- done
+
+return CrossingMinimizationGansnerKNV1993
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/layered/CycleRemovalBergerS1990a.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/layered/CycleRemovalBergerS1990a.lua
new file mode 100644
index 0000000000..76deb8adaf
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/layered/CycleRemovalBergerS1990a.lua
@@ -0,0 +1,74 @@
+-- Copyright 2011 by Jannis Pohlmann
+--
+-- 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
+
+-- @release $Header$
+
+
+local CycleRemovalBergerS1990a = {}
+
+local lib = require("pgf.gd.lib")
+
+
+function CycleRemovalBergerS1990a:run()
+ -- remember edges that were removed
+ local removed = {}
+
+ -- remember edges that need to be reversed
+ local reverse = {}
+
+ -- iterate over all nodes of the graph
+ for _,node in ipairs(self.graph.nodes) do
+ -- get all outgoing edges that have not been removed yet
+ local out_edges = lib.imap(node:getOutgoingEdges(),
+ function (edge)
+ if not removed[edge] then return edge end
+ end)
+
+ -- get all incoming edges that have not been removed yet
+ local in_edges = lib.imap(node:getIncomingEdges(),
+ function (edge)
+ if not removed[edge] then return edge end
+ end)
+
+ if #out_edges >= #in_edges then
+ -- we have more outgoing than incoming edges, reverse all incoming
+ -- edges and mark all incident edges as removed
+
+ for _,edge in ipairs(out_edges) do
+ removed[edge] = true
+ end
+ for _,edge in ipairs(in_edges) do
+ reverse[edge] = true
+ removed[edge] = true
+ end
+ else
+ -- we have more incoming than outgoing edges, reverse all outgoing
+ -- edges and mark all incident edges as removed
+
+ for _,edge in ipairs(out_edges) do
+ reverse[edge] = true
+ removed[edge] = true
+ end
+ for _,edge in ipairs(in_edges) do
+ removed[edge] = true
+ end
+ end
+ end
+
+ -- mark edges as reversed
+ for edge in pairs(reverse) do
+ edge.reversed = true
+ end
+end
+
+
+
+-- done
+
+return CycleRemovalBergerS1990a
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/layered/CycleRemovalBergerS1990b.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/layered/CycleRemovalBergerS1990b.lua
new file mode 100644
index 0000000000..d8f28980c8
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/layered/CycleRemovalBergerS1990b.lua
@@ -0,0 +1,76 @@
+-- Copyright 2011 by Jannis Pohlmann
+--
+-- 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
+
+-- @release $Header$
+
+
+local CycleRemovalBergerS1990b = {}
+
+local lib = require("pgf.gd.lib")
+
+
+function CycleRemovalBergerS1990b:run()
+ -- remember edges that were removed
+ local removed = {}
+
+ -- remember edges that need to be reversed
+ local reverse = {}
+
+ -- iterate over all nodes of the graph
+ for _,j in ipairs(lib.random_permutation(#self.graph.nodes)) do
+ local node = self.graph.nodes[j]
+ -- get all outgoing edges that have not been removed yet
+ -- get all outgoing edges that have not been removed yet
+ local out_edges = lib.imap(node:getOutgoingEdges(),
+ function (edge)
+ if not removed[edge] then return edge end
+ end)
+
+ -- get all incoming edges that have not been removed yet
+ local in_edges = lib.imap(node:getIncomingEdges(),
+ function (edge)
+ if not removed[edge] then return edge end
+ end)
+
+ if #out_edges >= #in_edges then
+ -- we have more outgoing than incoming edges, reverse all incoming
+ -- edges and mark all incident edges as removed
+
+ for _,edge in ipairs(out_edges) do
+ removed[edge] = true
+ end
+ for _,edge in ipairs(in_edges) do
+ reverse[edge] = true
+ removed[edge] = true
+ end
+ else
+ -- we have more incoming than outgoing edges, reverse all outgoing
+ -- edges and mark all incident edges as removed
+
+ for _,edge in ipairs(out_edges) do
+ reverse[edge] = true
+ removed[edge] = true
+ end
+ for _,edge in ipairs(in_edges) do
+ removed[edge] = true
+ end
+ end
+ end
+
+ -- mark edges as reversed
+ for edge in pairs(reverse) do
+ edge.reversed = true
+ end
+end
+
+
+
+-- done
+
+return CycleRemovalBergerS1990b
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/layered/CycleRemovalEadesLS1993.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/layered/CycleRemovalEadesLS1993.lua
new file mode 100644
index 0000000000..ffb919b209
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/layered/CycleRemovalEadesLS1993.lua
@@ -0,0 +1,124 @@
+-- Copyright 2011 by Jannis Pohlmann
+--
+-- 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
+
+-- @release $Header$
+
+
+
+-- Declare
+local CycleRemovalEadesLS1993 = {}
+
+-- Import
+local lib = require "pgf.gd.lib"
+
+
+function CycleRemovalEadesLS1993:run()
+ local copied_graph = self.graph:copy()
+
+ local copied_node = {}
+ local origin_node = {}
+ local copied_edge = {}
+ local origin_edge = {}
+
+ local preserve = {}
+
+ for _,edge in ipairs(self.graph.edges) do
+ copied_edge[edge] = edge:copy()
+ origin_edge[copied_edge[edge]] = edge
+
+ for _,node in ipairs(edge.nodes) do
+ if copied_node[node] then
+ copied_edge[edge]:addNode(copied_node[node])
+ else
+ copied_node[node] = node:copy()
+ origin_node[copied_node[node]] = node
+
+ copied_graph:addNode(copied_node[node])
+ copied_edge[edge]:addNode(copied_node[node])
+ end
+ end
+ end
+
+ local function node_is_sink(node)
+ return node:getOutDegree() == 0
+ end
+
+ local function node_is_source(node)
+ return node:getInDegree() == 0
+ end
+
+ local function node_is_isolated(node)
+ return node:getDegree() == 0
+ end
+
+ while #copied_graph.nodes > 0 do
+ local sink = lib.find(copied_graph.nodes, node_is_sink)
+ while sink do
+ for _,edge in ipairs(sink:getIncomingEdges()) do
+ preserve[edge] = true
+ end
+ copied_graph:deleteNode(sink)
+ sink = lib.find(copied_graph.nodes, node_is_sink)
+ end
+
+ local isolated_node = lib.find(copied_graph.nodes, node_is_isolated)
+ while isolated_node do
+ copied_graph:deleteNode(isolated_node)
+ isolated_node = lib.find(copied_graph.nodes, node_is_isolated)
+ end
+
+ local source = lib.find(copied_graph.nodes, node_is_source)
+ while source do
+ for _,edge in ipairs(source:getOutgoingEdges()) do
+ preserve[edge] = true
+ end
+ copied_graph:deleteNode(source)
+ source = lib.find(copied_graph.nodes, node_is_source)
+ end
+
+ if #copied_graph.nodes > 0 then
+ local max_node = nil
+ local max_out_edges = nil
+ local max_in_edges = nil
+
+ for _,node in ipairs(copied_graph.nodes) do
+ local out_edges = node:getOutgoingEdges()
+ local in_edges = node:getIncomingEdges()
+
+ if max_node == nil or (#out_edges - #in_edges > #max_out_edges - #max_in_edges) then
+ max_node = node
+ max_out_edges = out_edges
+ max_in_edges = in_edges
+ end
+ end
+
+ assert(max_node and max_out_edges and max_in_edges)
+
+ for _,edge in ipairs(max_out_edges) do
+ preserve[edge] = true
+ copied_graph:deleteEdge(edge)
+ end
+ for _,edge in ipairs(max_in_edges) do
+ copied_graph:deleteEdge(edge)
+ end
+
+ copied_graph:deleteNode(max_node)
+ end
+ end
+
+ for _,edge in ipairs(self.graph.edges) do
+ if not preserve[copied_edge[edge]] then
+ edge.reversed = true
+ end
+ end
+end
+
+-- done
+
+return CycleRemovalEadesLS1993 \ No newline at end of file
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/layered/CycleRemovalGansnerKNV1993.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/layered/CycleRemovalGansnerKNV1993.lua
new file mode 100644
index 0000000000..e52b0d38d1
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/layered/CycleRemovalGansnerKNV1993.lua
@@ -0,0 +1,52 @@
+-- Copyright 2011 by Jannis Pohlmann and 2012 by 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
+
+-- @release $Header$
+
+
+local CycleRemovalGansnerKNV1993 = {}
+
+
+-- Imports
+local declare = require("pgf.gd.interface.InterfaceToAlgorithms").declare
+local Simplifiers = require "pgf.gd.lib.Simplifiers"
+
+
+function CycleRemovalGansnerKNV1993:run ()
+ -- merge nonempty sets into supernodes
+ --
+ -- ignore self-loops
+ --
+ -- merge multiple edges into one edge each, whose weight is the sum of the
+ -- individual edge weights
+ --
+ -- ignore leaf nodes that are not part of the user-defined sets (their ranks
+ -- are trivially determined)
+ --
+ -- ensure that supernodes S_min and S_max are assigned first and last ranks
+ -- reverse in-edges of S_min
+ -- reverse out-edges of S_max
+ --
+ -- ensure the supernodes S_min and S_max are are the only nodes in these ranks
+ -- for all nodes with indegree of 0, insert temporary edge (S_min, v) with delta=0
+ -- for all nodes with outdegree of 0, insert temporary edge (v, S_max) with delta=0
+
+ -- classify edges as tree/forward, cross and back edges using a DFS traversal
+ local tree_or_forward_edges, cross_edges, back_edges = Simplifiers:classifyEdges(self.graph)
+
+ -- reverse the back edges in order to make the graph acyclic
+ for _,edge in ipairs(back_edges) do
+ edge.reversed = true
+ end
+end
+
+
+-- done
+
+return CycleRemovalGansnerKNV1993 \ No newline at end of file
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/layered/EdgeRoutingGansnerKNV1993.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/layered/EdgeRoutingGansnerKNV1993.lua
new file mode 100644
index 0000000000..c451f74116
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/layered/EdgeRoutingGansnerKNV1993.lua
@@ -0,0 +1,22 @@
+-- Copyright 2011 by Jannis Pohlmann
+--
+-- 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
+
+-- @release $Header$
+
+
+
+local EdgeRoutingGansnerKNV1993 = {}
+
+
+function EdgeRoutingGansnerKNV1993:run()
+end
+
+
+-- done
+return EdgeRoutingGansnerKNV1993 \ No newline at end of file
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/layered/NetworkSimplex.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/layered/NetworkSimplex.lua
new file mode 100644
index 0000000000..0ccf7694ca
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/layered/NetworkSimplex.lua
@@ -0,0 +1,898 @@
+-- Copyright 2011 by Jannis Pohlmann
+-- 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$
+
+
+
+
+--- This file contains an implementation of the network simplex method
+--- for node ranking and x coordinate optimization in layered drawing
+--- algorithms, as proposed in
+---
+--- "A Technique for Drawing Directed Graphs"
+-- by Gansner, Koutsofios, North, Vo, 1993.
+
+
+local NetworkSimplex = {}
+NetworkSimplex.__index = NetworkSimplex
+
+-- Namespace
+local layered = require "pgf.gd.layered"
+layered.NetworkSimplex = NetworkSimplex
+
+
+-- Imports
+local DepthFirstSearch = require "pgf.gd.lib.DepthFirstSearch"
+local Ranking = require "pgf.gd.layered.Ranking"
+local Graph = require "pgf.gd.deprecated.Graph"
+local lib = require "pgf.gd.lib"
+
+
+
+-- Definitions
+
+NetworkSimplex.BALANCE_TOP_BOTTOM = 1
+NetworkSimplex.BALANCE_LEFT_RIGHT = 2
+
+
+function NetworkSimplex.new(graph, balancing)
+ local simplex = {
+ graph = graph,
+ balancing = balancing,
+ }
+ setmetatable(simplex, NetworkSimplex)
+ return simplex
+end
+
+
+
+function NetworkSimplex:run()
+
+ assert (#self.graph.nodes > 0, "graph must contain at least one node")
+
+ -- initialize the tree edge search index
+ self.search_index = 1
+
+ -- initialize internal edge parameters
+ self.cut_value = {}
+ for _,edge in ipairs(self.graph.edges) do
+ self.cut_value[edge] = 0
+ end
+
+ -- reset graph information needed for ranking
+ self.lim = {}
+ self.low = {}
+ self.parent_edge = {}
+ self.ranking = Ranking.new()
+
+ if #self.graph.nodes == 1 then
+ self.ranking:setRank(self.graph.nodes[1], 1)
+ else
+ self:rankNodes()
+ end
+end
+
+
+
+function NetworkSimplex:rankNodes()
+ -- construct feasible tree of tight edges
+ self:constructFeasibleTree()
+
+ -- iteratively replace edges with negative cut values
+ -- with non-tree edges (chosen by minimum slack)
+ local leave_edge = self:findNegativeCutEdge()
+ while leave_edge do
+ local enter_edge = self:findReplacementEdge(leave_edge)
+
+ assert(enter_edge, 'no non-tree edge to replace ' .. tostring(leave_edge) .. ' could be found')
+
+ -- exchange leave_edge and enter_edge in the tree, updating
+ -- the ranks and cut values of all nodes
+ self:exchangeTreeEdges(leave_edge, enter_edge)
+
+ -- find the next tree edge with a negative cut value, if
+ -- there are any left
+ leave_edge = self:findNegativeCutEdge()
+ end
+
+ if self.balancing == NetworkSimplex.BALANCE_TOP_BOTTOM then
+ -- normalize by setting the least rank to zero
+ self.ranking:normalizeRanks()
+
+ -- move nodes to feasible ranks with the least number of nodes
+ -- in order to avoid crowding and to improve the overall aspect
+ -- ratio of the drawing
+ self:balanceRanksTopBottom()
+ elseif self.balancing == NetworkSimplex.BALANCE_LEFT_RIGHT then
+ self:balanceRanksLeftRight()
+ end
+end
+
+
+
+function NetworkSimplex:constructFeasibleTree()
+
+ self:computeInitialRanking()
+
+ -- find a maximal tree of tight edges in the graph
+ while self:findTightTree() < #self.graph.nodes do
+
+ local min_slack_edge = nil
+
+ for _,node in ipairs(self.graph.nodes) do
+ local out_edges = node:getOutgoingEdges()
+ for _,edge in ipairs(out_edges) do
+ if not self.tree_edge[edge] and self:isIncidentToTree(edge) then
+ if not min_slack_edge or self:edgeSlack(edge) < self:edgeSlack(min_slack_edge) then
+ min_slack_edge = edge
+ end
+ end
+ end
+ end
+
+ if min_slack_edge then
+ local delta = self:edgeSlack(min_slack_edge)
+
+ if delta > 0 then
+ local head = min_slack_edge:getHead()
+ local tail = min_slack_edge:getTail()
+
+ if self.tree_node[head] then
+ delta = -delta
+ end
+
+
+ for _,node in ipairs(self.tree.nodes) do
+ local rank = self.ranking:getRank(self.orig_node[node])
+ self.ranking:setRank(self.orig_node[node], rank + delta)
+ end
+ end
+ end
+ end
+
+ self:initializeCutValues()
+end
+
+
+
+function NetworkSimplex:findNegativeCutEdge()
+ local minimum_edge = nil
+
+ for n=1,#self.tree.edges do
+ local index = self:nextSearchIndex()
+
+ local edge = self.tree.edges[index]
+
+ if self.cut_value[edge] < 0 then
+ if minimum_edge then
+ if self.cut_value[minimum_edge] > self.cut_value[edge] then
+ minimum_edge = edge
+ end
+ else
+ minimum_edge = edge
+ end
+ end
+ end
+
+ return minimum_edge
+end
+
+
+
+function NetworkSimplex:findReplacementEdge(leave_edge)
+ local tail = leave_edge:getTail()
+ local head = leave_edge:getHead()
+
+ local v = nil
+ local direction = nil
+
+ if self.lim[tail] < self.lim[head] then
+ v = tail
+ direction = 'in'
+ else
+ v = head
+ direction = 'out'
+ end
+
+ local search_root = v
+ local enter_edge = nil
+ local slack = math.huge
+
+ -- TODO Jannis: Get rid of this recursion:
+
+ local function find_edge(v, direction)
+
+ if direction == 'out' then
+ local out_edges = self.orig_node[v]:getOutgoingEdges()
+ for _,edge in ipairs(out_edges) do
+ local head = edge:getHead()
+ local tree_head = self.tree_node[head]
+
+ assert(head and tree_head)
+
+ if not self.tree_edge[edge] then
+ if not self:inTailComponentOf(tree_head, search_root) then
+ if self:edgeSlack(edge) < slack or not enter_edge then
+ enter_edge = edge
+ slack = self:edgeSlack(edge)
+ end
+ end
+ else
+ if self.lim[tree_head] < self.lim[v] then
+ find_edge(tree_head, 'out')
+ end
+ end
+ end
+
+ for _,edge in ipairs(v:getIncomingEdges()) do
+ if slack <= 0 then
+ break
+ end
+
+ local tail = edge:getTail()
+
+ if self.lim[tail] < self.lim[v] then
+ find_edge(tail, 'out')
+ end
+ end
+ else
+ local in_edges = self.orig_node[v]:getIncomingEdges()
+ for _,edge in ipairs(in_edges) do
+ local tail = edge:getTail()
+ local tree_tail = self.tree_node[tail]
+
+ assert(tail and tree_tail)
+
+ if not self.tree_edge[edge] then
+ if not self:inTailComponentOf(tree_tail, search_root) then
+ if self:edgeSlack(edge) < slack or not enter_edge then
+ enter_edge = edge
+ slack = self:edgeSlack(edge)
+ end
+ end
+ else
+ if self.lim[tree_tail] < self.lim[v] then
+ find_edge(tree_tail, 'in')
+ end
+ end
+ end
+
+ for _,edge in ipairs(v:getOutgoingEdges()) do
+ if slack <= 0 then
+ break
+ end
+
+ local head = edge:getHead()
+
+ if self.lim[head] < self.lim[v] then
+ find_edge(head, 'in')
+ end
+ end
+ end
+ end
+
+ find_edge(v, direction)
+
+ return enter_edge
+end
+
+
+
+function NetworkSimplex:exchangeTreeEdges(leave_edge, enter_edge)
+
+ self:rerankBeforeReplacingEdge(leave_edge, enter_edge)
+
+ local cutval = self.cut_value[leave_edge]
+ local head = self.tree_node[enter_edge:getHead()]
+ local tail = self.tree_node[enter_edge:getTail()]
+
+ local ancestor = self:updateCutValuesUpToCommonAncestor(tail, head, cutval, true)
+ local other_ancestor = self:updateCutValuesUpToCommonAncestor(head, tail, cutval, false)
+
+ assert(ancestor == other_ancestor)
+
+ -- remove the old edge from the tree
+ self:removeEdgeFromTree(leave_edge)
+
+ -- add the new edge to the tree
+ local tree_edge = self:addEdgeToTree(enter_edge)
+
+ -- set its cut value
+ self.cut_value[tree_edge] = -cutval
+
+ -- update DFS search tree traversal information
+ self:calculateDFSRange(ancestor, self.parent_edge[ancestor], self.low[ancestor])
+end
+
+
+
+function NetworkSimplex:balanceRanksTopBottom()
+
+ -- available ranks
+ local ranks = self.ranking:getRanks()
+
+ -- node to in/out weight mappings
+ local in_weight = {}
+ local out_weight = {}
+
+ -- node to lowest/highest possible rank mapping
+ local min_rank = {}
+ local max_rank = {}
+
+ -- compute the in and out weights of each node
+ for _,node in ipairs(self.graph.nodes) do
+ -- assume there are no restrictions on how to rank the node
+ min_rank[node], max_rank[node] = ranks[1], ranks[#ranks]
+
+ for _,edge in ipairs(node:getIncomingEdges()) do
+ -- accumulate the weights of all incoming edges
+ in_weight[node] = (in_weight[node] or 0) + edge.weight
+
+ -- update the minimum allowed rank (which is the maximum of
+ -- the ranks of all parent neighbors plus the minimum level
+ -- separation caused by the connecting edges)
+ local neighbour = edge:getNeighbour(node)
+ local neighbour_rank = self.ranking:getRank(neighbour)
+ min_rank[node] = math.max(min_rank[node], neighbour_rank + edge.minimum_levels)
+ end
+
+ for _,edge in ipairs(node:getOutgoingEdges()) do
+ -- accumulate the weights of all outgoing edges
+ out_weight[node] = (out_weight[node] or 0) + edge.weight
+
+ -- update the maximum allowed rank (which is the minimum of
+ -- the ranks of all child neighbors minus the minimum level
+ -- separation caused by the connecting edges)
+ local neighbour = edge:getNeighbour(node)
+ local neighbour_rank = self.ranking:getRank(neighbour)
+ max_rank[node] = math.min(max_rank[node], neighbour_rank - edge.minimum_levels)
+ end
+
+ -- check whether the in- and outweight is the same
+ if in_weight[node] == out_weight[node] then
+
+ -- check which of the allowed ranks has the least number of nodes
+ local min_nodes_rank = min_rank[node]
+ for n = min_rank[node] + 1, max_rank[node] do
+ if #self.ranking:getNodes(n) < #self.ranking:getNodes(min_nodes_rank) then
+ min_nodes_rank = n
+ end
+ end
+
+ -- only move the node to the rank with the least number of nodes
+ -- if it differs from the current rank of the node
+ if min_nodes_rank ~= self.ranking:getRank(node) then
+ self.ranking:setRank(node, min_nodes_rank)
+ end
+
+ end
+ end
+end
+
+
+
+function NetworkSimplex:balanceRanksLeftRight()
+ for _,edge in ipairs(self.tree.edges) do
+ if self.cut_value[edge] == 0 then
+ local other_edge = self:findReplacementEdge(edge)
+ if other_edge then
+ local delta = self:edgeSlack(other_edge)
+ if delta > 1 then
+ if self.lim[edge:getTail()] < self.lim[edge:getHead()] then
+ self:rerank(edge:getTail(), delta / 2)
+ else
+ self:rerank(edge:getHead(), -delta / 2)
+ end
+ end
+ end
+ end
+ end
+end
+
+
+
+function NetworkSimplex:computeInitialRanking()
+
+ -- queue for nodes to rank next
+ local queue = {}
+
+ -- convenience functions for managing the queue
+ local function enqueue(node) table.insert(queue, node) end
+ local function dequeue() return table.remove(queue, 1) end
+
+ -- reset the two-dimensional mapping from ranks to lists
+ -- of corresponding nodes
+ self.ranking:reset()
+
+ -- mapping of nodes to the number of unscanned incoming edges
+ local remaining_edges = {}
+
+ -- add all sinks to the queue
+ for _,node in ipairs(self.graph.nodes) do
+ local edges = node:getIncomingEdges()
+
+ remaining_edges[node] = #edges
+
+ if #edges == 0 then
+ enqueue(node)
+ end
+ end
+
+ -- run long as there are nodes to be ranked
+ while #queue > 0 do
+
+ -- fetch the next unranked node from the queue
+ local node = dequeue()
+
+ -- get a list of its incoming edges
+ local in_edges = node:getIncomingEdges()
+
+ -- determine the minimum possible rank for the node
+ local rank = 1
+ for _,edge in ipairs(in_edges) do
+ local neighbour = edge:getNeighbour(node)
+ if self.ranking:getRank(neighbour) then
+ -- the minimum possible rank is the maximum of all neighbor ranks plus
+ -- the corresponding edge lengths
+ rank = math.max(rank, self.ranking:getRank(neighbour) + edge.minimum_levels)
+ end
+ end
+
+ -- rank the node
+ self.ranking:setRank(node, rank)
+
+ -- get a list of the node's outgoing edges
+ local out_edges = node:getOutgoingEdges()
+
+ -- queue neighbors of nodes for which all incoming edges have been scanned
+ for _,edge in ipairs(out_edges) do
+ local head = edge:getHead()
+ remaining_edges[head] = remaining_edges[head] - 1
+ if remaining_edges[head] <= 0 then
+ enqueue(head)
+ end
+ end
+ end
+end
+
+
+
+function NetworkSimplex:findTightTree()
+
+ -- TODO: Jannis: Remove the recursion below:
+
+ local marked = {}
+
+ local function build_tight_tree(node)
+
+ local out_edges = node:getOutgoingEdges()
+ local in_edges = node:getIncomingEdges()
+
+ local edges = lib.copy(out_edges)
+ for _,v in ipairs(in_edges) do
+ edges[#edges + 1] = v
+ end
+
+ for _,edge in ipairs(edges) do
+ local neighbour = edge:getNeighbour(node)
+ if (not marked[neighbour]) and math.abs(self:edgeSlack(edge)) < 0.00001 then
+ self:addEdgeToTree(edge)
+
+ for _,node in ipairs(edge.nodes) do
+ marked[node] = true
+ end
+
+ if #self.tree.edges == #self.graph.nodes-1 then
+ return true
+ end
+
+ if build_tight_tree(neighbour) then
+ return true
+ end
+ end
+ end
+
+ return false
+ end
+
+ for _,node in ipairs(self.graph.nodes) do
+ self.tree = Graph.new()
+ self.tree_node = {}
+ self.orig_node = {}
+ self.tree_edge = {}
+ self.orig_edge = {}
+
+ build_tight_tree(node)
+
+ if #self.tree.edges > 0 then
+ break
+ end
+ end
+
+ return #self.tree.nodes
+end
+
+
+
+function NetworkSimplex:edgeSlack(edge)
+ -- make sure this is never called with a tree edge
+ assert(not self.orig_edge[edge])
+
+ local head_rank = self.ranking:getRank(edge:getHead())
+ local tail_rank = self.ranking:getRank(edge:getTail())
+ local length = head_rank - tail_rank
+ return length - edge.minimum_levels
+end
+
+
+
+function NetworkSimplex:isIncidentToTree(edge)
+ -- make sure this is never called with a tree edge
+ assert(not self.orig_edge[edge])
+
+ local head = edge:getHead()
+ local tail = edge:getTail()
+
+ if self.tree_node[head] and not self.tree_node[tail] then
+ return true
+ elseif self.tree_node[tail] and not self.tree_node[head] then
+ return true
+ else
+ return false
+ end
+end
+
+
+
+function NetworkSimplex:initializeCutValues()
+ self:calculateDFSRange(self.tree.nodes[1], nil, 1)
+
+ local function init(search)
+ search:push({ node = self.tree.nodes[1], parent_edge = nil })
+ end
+
+ local function visit(search, data)
+ search:setVisited(data, true)
+
+ local into = data.node:getIncomingEdges()
+ local out = data.node:getOutgoingEdges()
+
+ for i=#into,1,-1 do
+ local edge = into[i]
+ if edge ~= data.parent_edge then
+ search:push({ node = edge:getTail(), parent_edge = edge })
+ end
+ end
+
+ for i=#out,1,-1 do
+ local edge = out[i]
+ if edge ~= data.parent_edge then
+ search:push({ node = edge:getHead(), parent_edge = edge })
+ end
+ end
+ end
+
+ local function complete(search, data)
+ if data.parent_edge then
+ self:updateCutValue(data.parent_edge)
+ end
+ end
+
+ DepthFirstSearch.new(init, visit, complete):run()
+end
+
+
+
+--- DFS algorithm that calculates post-order traversal indices and parent edges.
+--
+-- This algorithm performs a depth-first search in a directed or undirected
+-- graph. For each node it calculates the node's post-order traversal index, the
+-- minimum post-order traversal index of its descendants as well as the edge by
+-- which the node was reached in the depth-first traversal.
+--
+function NetworkSimplex:calculateDFSRange(root, edge_from_parent, lowest)
+
+ -- global traversal index counter
+ local lim = lowest
+
+ -- start the traversal at the root node
+ local function init(search)
+ search:push({ node = root, parent_edge = edge_from_parent, low = lowest })
+ end
+
+ -- visit nodes in depth-first order
+ local function visit(search, data)
+ -- mark node as visited so we only visit it once
+ search:setVisited(data, true)
+
+ -- remember the parent edge
+ self.parent_edge[data.node] = data.parent_edge
+
+ -- remember the minimum traversal index for this branch of the search tree
+ self.low[data.node] = lim
+
+ -- next we push all outgoing and incoming edges in reverse order
+ -- to simulate recursive calls
+
+ local into = data.node:getIncomingEdges()
+ local out = data.node:getOutgoingEdges()
+
+ for i=#into,1,-1 do
+ local edge = into[i]
+ if edge ~= data.parent_edge then
+ search:push({ node = edge:getTail(), parent_edge = edge })
+ end
+ end
+
+ for i=#out,1,-1 do
+ local edge = out[i]
+ if edge ~= data.parent_edge then
+ search:push({ node = edge:getHead(), parent_edge = edge })
+ end
+ end
+ end
+
+ -- when completing a node, store its own traversal index
+ local function complete(search, data)
+ self.lim[data.node] = lim
+ lim = lim + 1
+ end
+
+ -- kick off the depth-first search
+ DepthFirstSearch.new(init, visit, complete):run()
+
+ local lim_lookup = {}
+ local min_lim = math.huge
+ local max_lim = -math.huge
+ for _,node in ipairs(self.tree.nodes) do
+ assert(self.lim[node])
+ assert(self.low[node])
+ assert(not lim_lookup[self.lim[node]])
+ lim_lookup[self.lim[node]] = true
+ min_lim = math.min(min_lim, self.lim[node])
+ max_lim = math.max(max_lim, self.lim[node])
+ end
+ for n = min_lim, max_lim do
+ assert(lim_lookup[n] == true)
+ end
+end
+
+
+
+function NetworkSimplex:updateCutValue(tree_edge)
+
+ local v = nil
+ if self.parent_edge[tree_edge:getTail()] == tree_edge then
+ v = tree_edge:getTail()
+ dir = 1
+ else
+ v = tree_edge:getHead()
+ dir = -1
+ end
+
+ local sum = 0
+
+ local out_edges = self.orig_node[v]:getOutgoingEdges()
+ local in_edges = self.orig_node[v]:getIncomingEdges()
+ local edges = lib.copy(out_edges)
+ for _,v in ipairs(in_edges) do
+ edges[#edges + 1] = v
+ end
+
+ for _,edge in ipairs(edges) do
+ local other = edge:getNeighbour(self.orig_node[v])
+
+ local f = 0
+ local rv = 0
+
+ if not self:inTailComponentOf(self.tree_node[other], v) then
+ f = 1
+ rv = edge.weight
+ else
+ f = 0
+
+ if self.tree_edge[edge] then
+ rv = self.cut_value[self.tree_edge[edge]]
+ else
+ rv = 0
+ end
+
+ rv = rv - edge.weight
+ end
+
+ local d = 0
+
+ if dir > 0 then
+ if edge:isHead(self.orig_node[v]) then
+ d = 1
+ else
+ d = -1
+ end
+ else
+ if edge:isTail(self.orig_node[v]) then
+ d = 1
+ else
+ d = -1
+ end
+ end
+
+ if f > 0 then
+ d = -d
+ end
+
+ if d < 0 then
+ rv = -rv
+ end
+
+ sum = sum + rv
+ end
+
+ self.cut_value[tree_edge] = sum
+end
+
+
+
+function NetworkSimplex:inTailComponentOf(node, v)
+ return (self.low[v] <= self.lim[node]) and (self.lim[node] <= self.lim[v])
+end
+
+
+
+function NetworkSimplex:nextSearchIndex()
+ local index = 1
+
+ -- avoid tree edge index out of bounds by resetting the search index
+ -- as soon as it leaves the range of edge indices in the tree
+ if self.search_index > #self.tree.edges then
+ self.search_index = 1
+ index = 1
+ else
+ index = self.search_index
+ self.search_index = self.search_index + 1
+ end
+
+ return index
+end
+
+
+
+function NetworkSimplex:rerank(node, delta)
+ local function init(search)
+ search:push({ node = node, delta = delta })
+ end
+
+ local function visit(search, data)
+ search:setVisited(data, true)
+
+ local orig_node = self.orig_node[data.node]
+ self.ranking:setRank(orig_node, self.ranking:getRank(orig_node) - data.delta)
+
+ local into = data.node:getIncomingEdges()
+ local out = data.node:getOutgoingEdges()
+
+ for i=#into,1,-1 do
+ local edge = into[i]
+ if edge ~= self.parent_edge[data.node] then
+ search:push({ node = edge:getTail(), delta = data.delta })
+ end
+ end
+
+ for i=#out,1,-1 do
+ local edge = out[i]
+ if edge ~= self.parent_edge[data.node] then
+ search:push({ node = edge:getHead(), delta = data.delta })
+ end
+ end
+ end
+
+ DepthFirstSearch.new(init, visit):run()
+end
+
+
+
+function NetworkSimplex:rerankBeforeReplacingEdge(leave_edge, enter_edge)
+ local delta = self:edgeSlack(enter_edge)
+
+ if delta > 0 then
+ local tail = leave_edge:getTail()
+
+ if #tail.edges == 1 then
+ self:rerank(tail, delta)
+ else
+ local head = leave_edge:getHead()
+
+ if #head.edges == 1 then
+ self:rerank(head, -delta)
+ else
+ if self.lim[tail] < self.lim[head] then
+ self:rerank(tail, delta)
+ else
+ self:rerank(head, -delta)
+ end
+ end
+ end
+ end
+end
+
+
+
+function NetworkSimplex:updateCutValuesUpToCommonAncestor(v, w, cutval, dir)
+
+ while not self:inTailComponentOf(w, v) do
+ local edge = self.parent_edge[v]
+
+ if edge:isTail(v) then
+ d = dir
+ else
+ d = not dir
+ end
+
+ if d then
+ self.cut_value[edge] = self.cut_value[edge] + cutval
+ else
+ self.cut_value[edge] = self.cut_value[edge] - cutval
+ end
+
+ if self.lim[edge:getTail()] > self.lim[edge:getHead()] then
+ v = edge:getTail()
+ else
+ v = edge:getHead()
+ end
+ end
+
+ return v
+end
+
+
+
+function NetworkSimplex:addEdgeToTree(edge)
+ assert(not self.tree_edge[edge])
+
+ -- create the new tree edge
+ local tree_edge = edge:copy()
+ self.orig_edge[tree_edge] = edge
+ self.tree_edge[edge] = tree_edge
+
+ -- create tree nodes if necessary
+ for _,node in ipairs(edge.nodes) do
+ local tree_node
+
+ if self.tree_node[node] then
+ tree_node = self.tree_node[node]
+ else
+ tree_node = node:copy()
+ self.orig_node[tree_node] = node
+ self.tree_node[node] = tree_node
+ end
+
+ self.tree:addNode(tree_node)
+ tree_edge:addNode(tree_node)
+ end
+
+ self.tree:addEdge(tree_edge)
+
+ return tree_edge
+end
+
+
+
+function NetworkSimplex:removeEdgeFromTree(edge)
+ self.tree:deleteEdge(edge)
+ self.tree_edge[self.orig_edge[edge]] = nil
+ self.orig_edge[edge] = nil
+end
+
+
+
+
+-- Done
+
+return NetworkSimplex \ No newline at end of file
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/layered/NodePositioningGansnerKNV1993.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/layered/NodePositioningGansnerKNV1993.lua
new file mode 100644
index 0000000000..3e5a0f8f87
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/layered/NodePositioningGansnerKNV1993.lua
@@ -0,0 +1,154 @@
+-- Copyright 2011 by Jannis Pohlmann
+--
+-- 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
+
+-- @release $Header$
+
+
+
+local NodePositioningGansnerKNV1993 = {}
+
+
+-- Imports
+
+local layered = require "pgf.gd.layered"
+
+local Graph = require "pgf.gd.deprecated.Graph"
+local Edge = require "pgf.gd.deprecated.Edge"
+local Node = require "pgf.gd.deprecated.Node"
+
+local NetworkSimplex = require "pgf.gd.layered.NetworkSimplex"
+local Storage = require "pgf.gd.lib.Storage"
+
+
+function NodePositioningGansnerKNV1993:run()
+ local auxiliary_graph = self:constructAuxiliaryGraph()
+
+ local simplex = NetworkSimplex.new(auxiliary_graph, NetworkSimplex.BALANCE_LEFT_RIGHT)
+ simplex:run()
+ local x_ranking = simplex.ranking
+
+ local layers = Storage.new()
+
+ local ranks = self.ranking:getRanks()
+ for _,rank in ipairs(ranks) do
+ local nodes = self.ranking:getNodes(rank)
+ for _,node in ipairs(nodes) do
+ node.pos.x = x_ranking:getRank(node.aux_node)
+ layers[node.orig_vertex] = rank
+ end
+ end
+
+ layered.arrange_layers_by_baselines(layers, self.main_algorithm.adjusted_bb, self.main_algorithm.ugraph)
+
+ -- Copy back
+ for _,rank in ipairs(ranks) do
+ local nodes = self.ranking:getNodes(rank)
+ for _,node in ipairs(nodes) do
+ node.pos.y = node.orig_vertex.pos.y
+ end
+ end
+end
+
+
+
+
+function NodePositioningGansnerKNV1993:constructAuxiliaryGraph()
+
+ local aux_graph = Graph.new()
+
+ local edge_node = {}
+
+ for _,node in ipairs(self.graph.nodes) do
+ local copy = Node.new{
+ name = node.name,
+ orig_node = node,
+ }
+ node.aux_node = copy
+ aux_graph:addNode(copy)
+ end
+
+ for i=#self.graph.edges,1,-1 do
+ local edge = self.graph.edges[i]
+ local node = Node.new{
+ name = '{' .. tostring(edge) .. '}',
+ }
+
+ aux_graph:addNode(node)
+
+ node.orig_edge = edge
+ edge_node[edge] = node
+
+ local head = edge:getHead()
+ local tail = edge:getTail()
+
+ local tail_edge = Edge.new{
+ direction = Edge.RIGHT,
+ minimum_levels = 0,
+ weight = edge.weight * self:getOmega(edge),
+ }
+ tail_edge:addNode(node)
+ tail_edge:addNode(tail.aux_node)
+ aux_graph:addEdge(tail_edge)
+
+ local head_edge = Edge.new{
+ direction = Edge.RIGHT,
+ minimum_levels = 0,
+ weight = edge.weight * self:getOmega(edge),
+ }
+ head_edge:addNode(node)
+ head_edge:addNode(head.aux_node)
+ aux_graph:addEdge(head_edge)
+ end
+
+ local ranks = self.ranking:getRanks()
+ for _,rank in ipairs(ranks) do
+ local nodes = self.ranking:getNodes(rank)
+ for n = 1, #nodes-1 do
+ local v = nodes[n]
+ local w = nodes[n+1]
+
+ local separator_edge = Edge.new{
+ direction = Edge.RIGHT,
+ minimum_levels = self:getDesiredHorizontalDistance(v, w),
+ weight = 0,
+ }
+ separator_edge:addNode(v.aux_node)
+ separator_edge:addNode(w.aux_node)
+ aux_graph:addEdge(separator_edge)
+ end
+ end
+
+ return aux_graph
+end
+
+
+
+function NodePositioningGansnerKNV1993:getOmega(edge)
+ local node1 = edge.nodes[1]
+ local node2 = edge.nodes[2]
+
+ if (node1.kind == "dummy") and (node2.kind == "dummy") then
+ return 8
+ elseif (node1.kind == "dummy") or (node2.kind == "dummy") then
+ return 2
+ else
+ return 1
+ end
+end
+
+
+
+function NodePositioningGansnerKNV1993:getDesiredHorizontalDistance(v, w)
+ return layered.ideal_sibling_distance(self.main_algorithm.adjusted_bb, self.graph.orig_digraph, v.orig_vertex, w.orig_vertex)
+end
+
+
+-- done
+
+return NodePositioningGansnerKNV1993 \ No newline at end of file
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/layered/NodeRankingGansnerKNV1993.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/layered/NodeRankingGansnerKNV1993.lua
new file mode 100644
index 0000000000..d6765ae2f0
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/layered/NodeRankingGansnerKNV1993.lua
@@ -0,0 +1,159 @@
+-- Copyright 2011 by Jannis Pohlmann
+--
+-- 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
+
+-- @release $Header$
+
+
+local NodeRankingGansnerKNV1993 = {}
+
+
+-- Imports
+
+local Edge = require "pgf.gd.deprecated.Edge"
+local Node = require "pgf.gd.deprecated.Node"
+
+local NetworkSimplex = require "pgf.gd.layered.NetworkSimplex"
+
+
+
+function NodeRankingGansnerKNV1993:run()
+
+ local simplex = NetworkSimplex.new(self.graph, NetworkSimplex.BALANCE_TOP_BOTTOM)
+ simplex:run()
+ self.ranking = simplex.ranking
+
+ return simplex.ranking
+end
+
+
+
+function NodeRankingGansnerKNV1993:mergeClusters()
+
+ self.cluster_nodes = {}
+ self.cluster_node = {}
+ self.cluster_edges = {}
+
+ self.original_nodes = {}
+ self.original_edges = {}
+
+ for _,cluster in ipairs(self.graph.clusters) do
+
+ local cluster_node = Node.new{
+ name = 'cluster@' .. cluster.name,
+ }
+ table.insert(self.cluster_nodes, cluster_node)
+
+ for _,node in ipairs(cluster.nodes) do
+ self.cluster_node[node] = cluster_node
+ table.insert(self.original_nodes, node)
+ end
+
+ self.graph:addNode(cluster_node)
+ end
+
+ for _,edge in ipairs(self.graph.edges) do
+ local tail = edge:getTail()
+ local head = edge:getHead()
+
+ if self.cluster_node[tail] or self.cluster_node[head] then
+ table.insert(self.original_edges, edge)
+
+ local cluster_edge = Edge.new{
+ direction = Edge.RIGHT,
+ weight = edge.weight,
+ minimum_levels = edge.minimum_levels,
+ }
+ table.insert(self.cluster_edges, cluster_edge)
+
+ if self.cluster_node[tail] then
+ cluster_edge:addNode(self.cluster_node[tail])
+ else
+ cluster_edge:addNode(tail)
+ end
+
+ if self.cluster_node[head] then
+ cluster_edge:addNode(self.cluster_node[head])
+ else
+ cluster_edge:addNode(head)
+ end
+
+ end
+ end
+
+ for _,edge in ipairs(self.cluster_edges) do
+ self.graph:addEdge(edge)
+ end
+
+ for _,edge in ipairs(self.original_edges) do
+ self.graph:deleteEdge(edge)
+ end
+
+ for _,node in ipairs(self.original_nodes) do
+ self.graph:deleteNode(node)
+ end
+end
+
+
+
+function NodeRankingGansnerKNV1993:createClusterEdges()
+ for n = 1, #self.cluster_nodes-1 do
+ local first_cluster = self.cluster_nodes[n]
+ local second_cluster = self.cluster_nodes[n+1]
+
+ local edge = Edge.new{
+ direction = Edge.RIGHT,
+ weight = 1,
+ minimum_levels = 1,
+ }
+
+ edge:addNode(first_cluster)
+ edge:addNode(second_cluster)
+
+ self.graph:addEdge(edge)
+
+ table.insert(self.cluster_edges, edge)
+ end
+end
+
+
+
+function NodeRankingGansnerKNV1993:removeClusterEdges()
+end
+
+
+
+function NodeRankingGansnerKNV1993:expandClusters()
+
+ for _,node in ipairs(self.original_nodes) do
+ assert(self.ranking:getRank(self.cluster_node[node]))
+ self.ranking:setRank(node, self.ranking:getRank(self.cluster_node[node]))
+ self.graph:addNode(node)
+ end
+
+ for _,edge in ipairs(self.original_edges) do
+ for _,node in ipairs(edge.nodes) do
+ node:addEdge(edge)
+ end
+ self.graph:addEdge(edge)
+ end
+
+ for _,node in ipairs(self.cluster_nodes) do
+ self.ranking:setRank(node, nil)
+ self.graph:deleteNode(node)
+ end
+
+ for _,edge in ipairs(self.cluster_edges) do
+ self.graph:deleteEdge(edge)
+ end
+end
+
+
+-- done
+
+return NodeRankingGansnerKNV1993 \ No newline at end of file
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/layered/NodeRankingMinimumHeight.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/layered/NodeRankingMinimumHeight.lua
new file mode 100644
index 0000000000..644b99c28a
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/layered/NodeRankingMinimumHeight.lua
@@ -0,0 +1,47 @@
+-- Copyright 2011 by Jannis Pohlmann
+--
+-- 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
+
+-- @release $Header$
+
+
+local NodeRankingMinimumHeight = {}
+
+-- Imports
+
+local Ranking = require "pgf.gd.layered.Ranking"
+local Iterators = require "pgf.gd.deprecated.Iterators"
+
+
+function NodeRankingMinimumHeight:run()
+ local ranking = Ranking.new()
+
+ for node in Iterators.topologicallySorted(self.graph) do
+ local edges = node:getIncomingEdges()
+
+ if #edges == 0 then
+ ranking:setRank(node, 1)
+ else
+ local max_rank = -math.huge
+ for _,edge in ipairs(edges) do
+ max_rank = math.max(max_rank, ranking:getRank(edge:getNeighbour(node)))
+ end
+
+ assert(max_rank >= 1)
+
+ ranking:setRank(node, max_rank + 1)
+ end
+ end
+
+ return ranking
+end
+
+
+-- done
+
+return NodeRankingMinimumHeight
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/layered/Ranking.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/layered/Ranking.lua
new file mode 100644
index 0000000000..6ed63b0249
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/layered/Ranking.lua
@@ -0,0 +1,291 @@
+-- Copyright 2011 by Jannis Pohlmann
+-- 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$
+
+
+
+--- The Ranking class is used by the Sugiyama algorithm to compute an
+-- ordering on the nodes of a layer
+
+local Ranking = {}
+Ranking.__index = Ranking
+
+-- Namespace
+local layered = require "pgf.gd.layered"
+layered.Ranking = Ranking
+
+
+local lib = require "pgf.gd.lib"
+
+
+-- TODO Jannis: document!
+
+
+function Ranking.new()
+ local ranking = {
+ rank_to_nodes = {},
+ node_to_rank = {},
+ position_in_rank = {},
+ }
+ setmetatable(ranking, Ranking)
+ return ranking
+end
+
+
+
+function Ranking:copy()
+ local copied_ranking = Ranking.new()
+
+ -- copy rank to nodes mapping
+ for rank, nodes in pairs(self.rank_to_nodes) do
+ copied_ranking.rank_to_nodes[rank] = lib.copy(self.rank_to_nodes[rank])
+ end
+
+ -- copy node to rank mapping
+ copied_ranking.node_to_rank = lib.copy(self.node_to_rank)
+
+ -- copy node to position in rank mapping
+ copied_ranking.position_in_rank = lib.copy(self.position_in_rank)
+
+ return copied_ranking
+end
+
+
+
+function Ranking:reset()
+ self.rank_to_nodes = {}
+ self.node_to_rank = {}
+ self.position_in_rank = {}
+end
+
+
+
+function Ranking:getRanks()
+ local ranks = {}
+ for rank, nodes in pairs(self.rank_to_nodes) do
+ table.insert(ranks, rank)
+ end
+ table.sort(ranks)
+ return ranks
+end
+
+
+
+function Ranking:getRankSize(rank)
+ if self.rank_to_nodes[rank] then
+ return #self.rank_to_nodes[rank]
+ else
+ return 0
+ end
+end
+
+
+
+function Ranking:getNodeInfo(node)
+ return self:getRank(node), self:getRankPosition(node)
+end
+
+
+
+function Ranking:getNodes(rank)
+ return self.rank_to_nodes[rank] or {}
+end
+
+
+
+function Ranking:getRank(node)
+ return self.node_to_rank[node]
+end
+
+
+
+function Ranking:setRank(node, new_rank)
+ local rank, pos = self:getNodeInfo(node)
+
+ if rank == new_rank then
+ return
+ end
+
+ if rank then
+ for n = pos+1, #self.rank_to_nodes[rank] do
+ local other_node = self.rank_to_nodes[rank][n]
+ self.position_in_rank[other_node] = self.position_in_rank[other_node]-1
+ end
+
+ table.remove(self.rank_to_nodes[rank], pos)
+ self.node_to_rank[node] = nil
+ self.position_in_rank[node] = nil
+
+ if #self.rank_to_nodes[rank] == 0 then
+ self.rank_to_nodes[rank] = nil
+ end
+ end
+
+ if new_rank then
+ self.rank_to_nodes[new_rank] = self.rank_to_nodes[new_rank] or {}
+ table.insert(self.rank_to_nodes[new_rank], node)
+ self.node_to_rank[node] = new_rank
+ self.position_in_rank[node] = #self.rank_to_nodes[new_rank]
+ end
+end
+
+
+
+function Ranking:getRankPosition(node)
+ return self.position_in_rank[node]
+end
+
+
+
+function Ranking:setRankPosition(node, new_pos)
+ local rank, pos = self:getNodeInfo(node)
+
+ assert((rank and pos) or ((not rank) and (not pos)))
+
+ if pos == new_pos then
+ return
+ end
+
+ if rank and pos then
+ for n = pos+1, #self.rank_to_nodes[rank] do
+ local other_node = self.rank_to_nodes[rank][n]
+ self.position_in_rank[other_node] = self.position_in_rank[other_node]-1
+ end
+
+ table.remove(self.rank_to_nodes[rank], pos)
+ self.node_to_rank[node] = nil
+ self.position_in_rank[node] = nil
+ end
+
+ if new_pos then
+ self.rank_to_nodes[rank] = self.rank_to_nodes[rank] or {}
+
+ for n = new_pos+1, #self.rank_to_nodes[rank] do
+ local other_node = self.rank_to_nodes[rank][new_pos]
+ self.position_in_rank[other_node] = self.position_in_rank[other_node]+1
+ end
+
+ table.insert(self.rank_to_nodes[rank], node)
+ self.node_to_rank[node] = rank
+ self.position_in_rank[node] = new_pos
+ end
+end
+
+
+
+function Ranking:normalizeRanks()
+
+ -- get the current ranks
+ local ranks = self:getRanks()
+
+ local min_rank = ranks[1]
+ local max_rank = ranks[#ranks]
+
+ -- clear ranks
+ self.rank_to_nodes = {}
+
+ -- iterate over all nodes and rerank them manually
+ for node in pairs(self.position_in_rank) do
+ local rank, pos = self:getNodeInfo(node)
+ local new_rank = rank - (min_rank - 1)
+
+ self.rank_to_nodes[new_rank] = self.rank_to_nodes[new_rank] or {}
+ self.rank_to_nodes[new_rank][pos] = node
+
+ self.node_to_rank[node] = new_rank
+ end
+end
+
+
+
+function Ranking:switchPositions(left_node, right_node)
+ local left_rank = self.node_to_rank[left_node]
+ local right_rank = self.node_to_rank[right_node]
+
+ assert(left_rank == right_rank, 'only positions of nodes in the same rank can be switched')
+
+ local left_pos = self.position_in_rank[left_node]
+ local right_pos = self.position_in_rank[right_node]
+
+ self.rank_to_nodes[left_rank][left_pos] = right_node
+ self.rank_to_nodes[left_rank][right_pos] = left_node
+
+ self.position_in_rank[left_node] = right_pos
+ self.position_in_rank[right_node] = left_pos
+end
+
+
+
+function Ranking:reorderRank(rank, get_index_func, is_fixed_func)
+ self:reorderTable(self.rank_to_nodes[rank], get_index_func, is_fixed_func)
+
+ for n = 1, #self.rank_to_nodes[rank] do
+ self.position_in_rank[self.rank_to_nodes[rank][n]] = n
+ end
+end
+
+
+
+function Ranking:reorderTable(input, get_index_func, is_fixed_func)
+ -- collect all allowed indices
+ local allowed_indices = {}
+ for n = 1, #input do
+ if not is_fixed_func(n, input[n]) then
+ table.insert(allowed_indices, n)
+ end
+ end
+
+ -- collect all desired indices; for each of these desired indices,
+ -- remember by which element it was requested
+ local desired_to_real_indices = {}
+ local sort_indices = {}
+ for n = 1, #input do
+ if not is_fixed_func(n, input[n]) then
+ local index = get_index_func(n, input[n])
+ if not desired_to_real_indices[index] then
+ desired_to_real_indices[index] = {}
+ table.insert(sort_indices, index)
+ end
+ table.insert(desired_to_real_indices[index], n)
+ end
+ end
+
+ -- sort the desired indices
+ table.sort(sort_indices)
+
+ -- compute the final indices by counting the final indices generated
+ -- prior to the current one and by mapping this number to the allowed
+ -- index with the same number
+ local final_indices = {}
+ local n = 1
+ for _,index in ipairs(sort_indices) do
+ local real_indices = desired_to_real_indices[index]
+ for _,real_index in ipairs(real_indices) do
+ final_indices[real_index] = allowed_indices[n]
+ n = n + 1
+ end
+ end
+
+ -- flat-copy the input table so that we can still access the elements
+ -- using their real index while overwriting the input table in-place
+ local input_copy = lib.copy(input)
+
+ -- move flexible elements to their final indices
+ for old_index, new_index in pairs(final_indices) do
+ input[new_index] = input_copy[old_index]
+ end
+end
+
+
+
+-- Done
+
+return Ranking \ No newline at end of file
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/layered/Sugiyama.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/layered/Sugiyama.lua
new file mode 100644
index 0000000000..c91818dc30
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/layered/Sugiyama.lua
@@ -0,0 +1,476 @@
+-- Copyright 2011 by Jannis Pohlmann, 2012 by 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
+
+-- @release $Header$
+
+
+
+---
+-- @section subsection {The Modular Sugiyama Method}
+--
+-- @end
+
+local Sugiyama = {}
+
+-- Namespace
+require("pgf.gd.layered").Sugiyama = Sugiyama
+
+-- Imports
+local layered = require "pgf.gd.layered"
+local declare = require("pgf.gd.interface.InterfaceToAlgorithms").declare
+
+local Ranking = require "pgf.gd.layered.Ranking"
+local Simplifiers = require "pgf.gd.lib.Simplifiers"
+
+-- Deprecated stuff. Need to get rid of it!
+local Edge = require "pgf.gd.deprecated.Edge"
+local Node = require "pgf.gd.deprecated.Node"
+
+local Iterators = require "pgf.gd.deprecated.Iterators"
+local Vector = require "pgf.gd.deprecated.Vector"
+
+
+
+---
+
+declare {
+ key = "layered layout",
+ algorithm = Sugiyama,
+
+ preconditions = {
+ connected = true,
+ loop_free = true,
+ },
+
+ postconditions = {
+ upward_oriented = true
+ },
+
+ old_graph_model = true,
+
+ summary = [["
+ The |layered layout| is the key used to select the modular Sugiyama
+ layout algorithm.
+ "]],
+ documentation = [["
+ This algorithm consists of five consecutive steps, each of which can be
+ configured independently of the other ones (how this is done is
+ explained later in this section). Naturally, the ``best'' heuristics
+ are selected by default, so there is typically no need to change the
+ settings, but what is the ``best'' method for one graph need not be
+ the best one for another graph.
+
+ As can be seen in the first example, the algorithm will not only
+ position the nodes of a graph, but will also perform an edge
+ routing. This will look visually quite pleasing if you add the
+ |rounded corners| option:
+ "]],
+ examples = {[["
+ \tikz \graph [layered layout, sibling distance=7mm]
+ {
+ a -> {
+ b,
+ c -> { d, e, f }
+ } ->
+ h ->
+ a
+ };
+ "]],[["
+ \tikz [rounded corners] \graph [layered layout, sibling distance=7mm]
+ {
+ a -> {
+ b,
+ c -> { d, e, f }
+ } ->
+ h ->
+ a
+ };
+ "]]
+ }
+}
+
+---
+
+declare {
+ key = "minimum layers",
+ type = "number",
+ initial = "1",
+
+ summary = [["
+ The minimum number of levels that an edge must span. It is a bit of
+ the opposite of the |weight| parameter: While a large |weight|
+ causes an edge to become shorter, a larger |minimum layers| value
+ causes an edge to be longer.
+ "]],
+ examples = [["
+ \tikz \graph [layered layout] {
+ a -- {b [> minimum layers=3], c, d} -- e -- a;
+ };
+ "]]
+}
+
+
+---
+
+declare {
+ key = "same layer",
+ layer = 0,
+
+ summary = [["
+ The |same layer| collection allows you to enforce that several nodes
+ a on the same layer of a layered layout (this option is also known
+ as |same rank|). You use it like this:
+ "]],
+ examples = {[["
+ \tikz \graph [layered layout] {
+ a -- b -- c -- d -- e;
+
+ { [same layer] a, b };
+ { [same layer] d, e };
+ };
+ "]],[["
+ \tikz [rounded corners] \graph [layered layout] {
+ 1972 -> 1976 -> 1978 -> 1980 -> 1982 -> 1984 -> 1986 -> 1988 -> 1990 -> future;
+
+ { [same layer] 1972, Thompson };
+ { [same layer] 1976, Mashey, Bourne },
+ { [same layer] 1978, Formshell, csh },
+ { [same layer] 1980, esh, vsh },
+ { [same layer] 1982, ksh, "System-V" },
+ { [same layer] 1984, v9sh, tcsh },
+ { [same layer] 1986, "ksh-i" },
+ { [same layer] 1988, KornShell ,Perl, rc },
+ { [same layer] 1990, tcl, Bash },
+ { [same layer] "future", POSIX, "ksh-POSIX" },
+
+ Thompson -> { Mashey, Bourne, csh -> tcsh},
+ Bourne -> { ksh, esh, vsh, "System-V", v9sh -> rc, Bash},
+ { "ksh-i", KornShell } -> Bash,
+ { esh, vsh, Formshell, csh } -> ksh,
+ { KornShell, "System-V" } -> POSIX,
+ ksh -> "ksh-i" -> KornShell -> "ksh-POSIX",
+ Bourne -> Formshell,
+
+ { [edge={draw=none}]
+ Bash -> tcl,
+ KornShell -> Perl
+ }
+ };
+ "]]
+ }
+}
+
+
+
+-- Implementation
+
+function Sugiyama:run()
+ if #self.graph.nodes <= 1 then
+ return
+ end
+
+ local options = self.digraph.options
+
+ local cycle_removal_algorithm_class = options.algorithm_phases['cycle removal']
+ local node_ranking_algorithm_class = options.algorithm_phases['node ranking']
+ local crossing_minimization_algorithm_class = options.algorithm_phases['crossing minimization']
+ local node_positioning_algorithm_class = options.algorithm_phases['node positioning']
+ local edge_routing_algorithm_class = options.algorithm_phases['layer edge routing']
+
+ self:preprocess()
+
+ -- Helper function for collapsing multiedges
+ local function collapse (m,e)
+ m.weight = (m.weight or 0) + e.weight
+ m.minimum_levels = math.max((m.minimum_levels or 0), e.minimum_levels)
+ end
+
+ -- Rank using cluster
+
+ -- Create a subalgorithm object. Needed so that removed loops
+ -- are not stored on top of removed loops from main call.
+ local cluster_subalgorithm = { graph = self.graph }
+ self.graph:registerAlgorithm(cluster_subalgorithm)
+
+ self:mergeClusters()
+
+ Simplifiers:removeLoopsOldModel(cluster_subalgorithm)
+ Simplifiers:collapseMultiedgesOldModel(cluster_subalgorithm, collapse)
+
+ cycle_removal_algorithm_class.new { main_algorithm = self, graph = self.graph }:run()
+ self.ranking = node_ranking_algorithm_class.new{ main_algorithm = self, graph = self.graph }:run()
+ self:restoreCycles()
+
+ Simplifiers:expandMultiedgesOldModel(cluster_subalgorithm)
+ Simplifiers:restoreLoopsOldModel(cluster_subalgorithm)
+
+ self:expandClusters()
+
+ -- Now do actual computation
+ Simplifiers:collapseMultiedgesOldModel(cluster_subalgorithm, collapse)
+ cycle_removal_algorithm_class.new{ main_algorithm = self, graph = self.graph }:run()
+ self:insertDummyNodes()
+
+ -- Main algorithm
+ crossing_minimization_algorithm_class.new{
+ main_algorithm = self,
+ graph = self.graph,
+ ranking = self.ranking
+ }:run()
+ node_positioning_algorithm_class.new{
+ main_algorithm = self,
+ graph = self.graph,
+ ranking = self.ranking
+ }:run()
+
+ -- Cleanup
+ self:removeDummyNodes()
+ Simplifiers:expandMultiedgesOldModel(cluster_subalgorithm)
+ edge_routing_algorithm_class.new{ main_algorithm = self, graph = self.graph }:run()
+ self:restoreCycles()
+
+end
+
+
+
+function Sugiyama:preprocess()
+ -- initialize edge parameters
+ for _,edge in ipairs(self.graph.edges) do
+ -- read edge parameters
+ edge.weight = edge:getOption('weight')
+ edge.minimum_levels = edge:getOption('minimum layers')
+
+ -- validate edge parameters
+ assert(edge.minimum_levels >= 0, 'the edge ' .. tostring(edge) .. ' needs to have a minimum layers value greater than or equal to 0')
+ end
+end
+
+
+
+function Sugiyama:insertDummyNodes()
+ -- enumerate dummy nodes using a globally unique numeric ID
+ local dummy_id = 1
+
+ -- keep track of the original edges removed
+ self.original_edges = {}
+
+ -- keep track of dummy nodes introduced
+ self.dummy_nodes = {}
+
+ for node in Iterators.topologicallySorted(self.graph) do
+ local in_edges = node:getIncomingEdges()
+
+ for _,edge in ipairs (in_edges) do
+ local neighbour = edge:getNeighbour(node)
+ local dist = self.ranking:getRank(node) - self.ranking:getRank(neighbour)
+
+ if dist > 1 then
+ local dummies = {}
+
+ for i=1,dist-1 do
+ local rank = self.ranking:getRank(neighbour) + i
+
+ local dummy = Node.new{
+ pos = Vector.new(),
+ name = 'dummy@' .. neighbour.name .. '@to@' .. node.name .. '@at@' .. rank,
+ kind = "dummy",
+ orig_vertex = pgf.gd.model.Vertex.new{}
+ }
+
+ dummy_id = dummy_id + 1
+
+ self.graph:addNode(dummy)
+ self.ugraph:add {dummy.orig_vertex}
+
+ self.ranking:setRank(dummy, rank)
+
+ table.insert(self.dummy_nodes, dummy)
+ table.insert(edge.bend_nodes, dummy)
+
+ table.insert(dummies, dummy)
+ end
+
+ table.insert(dummies, 1, neighbour)
+ table.insert(dummies, #dummies+1, node)
+
+ for i = 2, #dummies do
+ local source = dummies[i-1]
+ local target = dummies[i]
+
+ local dummy_edge = Edge.new{
+ direction = Edge.RIGHT,
+ reversed = false,
+ weight = edge.weight, -- TODO or should we divide the weight of the original edge by the number of virtual edges?
+ }
+
+ dummy_edge:addNode(source)
+ dummy_edge:addNode(target)
+
+ self.graph:addEdge(dummy_edge)
+ end
+
+ table.insert(self.original_edges, edge)
+ end
+ end
+ end
+
+ for _,edge in ipairs(self.original_edges) do
+ self.graph:deleteEdge(edge)
+ end
+end
+
+
+
+function Sugiyama:removeDummyNodes()
+ -- delete dummy nodes
+ for _,node in ipairs(self.dummy_nodes) do
+ self.graph:deleteNode(node)
+ end
+
+ -- add original edge again
+ for _,edge in ipairs(self.original_edges) do
+ -- add edge to the graph
+ self.graph:addEdge(edge)
+
+ -- add edge to the nodes
+ for _,node in ipairs(edge.nodes) do
+ node:addEdge(edge)
+ end
+
+ -- convert bend nodes to bend points for TikZ
+ for _,bend_node in ipairs(edge.bend_nodes) do
+ local point = bend_node.pos:copy()
+ table.insert(edge.bend_points, point)
+ end
+
+ if edge.reversed then
+ local bp = edge.bend_points
+ for i=1,#bp/2 do
+ local j = #bp + 1 - i
+ bp[i], bp[j] = bp[j], bp[i]
+ end
+ end
+
+ -- clear the list of bend nodes
+ edge.bend_nodes = {}
+ end
+end
+
+
+
+function Sugiyama:mergeClusters()
+
+ self.cluster_nodes = {}
+ self.cluster_node = {}
+ self.cluster_edges = {}
+ self.cluster_original_edges = {}
+ self.original_nodes = {}
+
+ for _,cluster in ipairs(self.graph.clusters) do
+
+ local cluster_node = cluster.nodes[1]
+ table.insert(self.cluster_nodes, cluster_node)
+
+ for n = 2, #cluster.nodes do
+ local other_node = cluster.nodes[n]
+ self.cluster_node[other_node] = cluster_node
+ table.insert(self.original_nodes, other_node)
+ end
+ end
+
+ for _,edge in ipairs(self.graph.edges) do
+ local tail = edge:getTail()
+ local head = edge:getHead()
+
+ if self.cluster_node[tail] or self.cluster_node[head] then
+ local cluster_edge = Edge.new{
+ direction = Edge.RIGHT,
+ weight = edge.weight,
+ minimum_levels = edge.minimum_levels,
+ }
+
+ if self.cluster_node[tail] then
+ cluster_edge:addNode(self.cluster_node[tail])
+ else
+ cluster_edge:addNode(tail)
+ end
+
+ if self.cluster_node[head] then
+ cluster_edge:addNode(self.cluster_node[head])
+ else
+ cluster_edge:addNode(head)
+ end
+
+ table.insert(self.cluster_edges, cluster_edge)
+ table.insert(self.cluster_original_edges, edge)
+ end
+ end
+
+ for n = 1, #self.cluster_nodes-1 do
+ local first_node = self.cluster_nodes[n]
+ local second_node = self.cluster_nodes[n+1]
+
+ local edge = Edge.new{
+ direction = Edge.RIGHT,
+ weight = 1,
+ minimum_levels = 1,
+ }
+
+ edge:addNode(first_node)
+ edge:addNode(second_node)
+
+ table.insert(self.cluster_edges, edge)
+ end
+
+ for _,node in ipairs(self.original_nodes) do
+ self.graph:deleteNode(node)
+ end
+ for _,edge in ipairs(self.cluster_edges) do
+ self.graph:addEdge(edge)
+ end
+ for _,edge in ipairs(self.cluster_original_edges) do
+ self.graph:deleteEdge(edge)
+ end
+end
+
+
+
+function Sugiyama:expandClusters()
+
+ for _,node in ipairs(self.original_nodes) do
+ self.ranking:setRank(node, self.ranking:getRank(self.cluster_node[node]))
+ self.graph:addNode(node)
+ end
+
+ for _,edge in ipairs(self.cluster_original_edges) do
+ for _,node in ipairs(edge.nodes) do
+ node:addEdge(edge)
+ end
+ self.graph:addEdge(edge)
+ end
+
+ for _,edge in ipairs(self.cluster_edges) do
+ self.graph:deleteEdge(edge)
+ end
+end
+
+
+function Sugiyama:restoreCycles()
+ for _,edge in ipairs(self.graph.edges) do
+ edge.reversed = false
+ end
+end
+
+
+
+
+
+-- done
+
+return Sugiyama
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/layered/crossing_minimization.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/layered/crossing_minimization.lua
new file mode 100644
index 0000000000..54a1fafb23
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/layered/crossing_minimization.lua
@@ -0,0 +1,81 @@
+-- Copyright 2012 by 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
+
+-- @release $Header$
+
+
+local declare = require("pgf.gd.interface.InterfaceToAlgorithms").declare
+
+
+---
+-- @section subsection {Crossing Minimization (Node Ordering)}
+--
+-- The number of edge crossings in a layered drawing is determined by
+-- the ordering of nodes at each of its layers. Therefore, crossing
+-- minimization is the problem of reordering the nodes at each layer
+-- so that the overall number of edge crossings is minimized. The
+-- crossing minimization step takes a proper layering where every edge
+-- connects nodes in neighbored layers, allowing algorithms to
+-- minimize crossings layer by layer rather than all at once. While
+-- this does not reduce the complexity of the problem, it does make it
+-- considerably easier to understand and implement. Techniques based
+-- on such an iterative approach are also known as layer-by-layer
+-- sweep methods. They are used in many popular heuristics due to
+-- their simplicity and the good results they produce.
+--
+-- Sweeping refers to moving up and down from one layer to the next,
+-- reducing crossings along the way. In layer-by-layer sweep methods,
+-- an initial node ordering for one of the layers is computed
+-- first. Depending on the sweep direction this can either be the
+-- first layer or the last; in rare occasions the layer in the middle
+-- is used instead. Followed by this, the actual layer-by-layer sweep
+-- is performed. Given an initial ordering for the first layer $L_1$, a
+-- downward sweep first holds the nodes in $L_1$ fixed while reordering
+-- the nodes in the second layer $L_2$ to reduce the number of
+-- crossings between $L_1$ and $L_2$. It then goes on to reorder the
+-- third layer while holding the second layer fixed. This is continued
+-- until all layers except for the first one have been
+-- examined. Upward sweeping and sweeping from the middle work
+-- analogous.
+--
+-- Obviously, the central aspect of the layer-by-layer sweep is how
+-- the nodes of a specific layer are reordered using a neighbored
+-- layer as a fixed reference. This problem is known as one-sided
+-- crossing minimization, which unfortunately is NP-hard. In the
+-- following various heuristics to solve this problem are
+-- presented.
+--
+-- For more details, please see Section 4.1.4 of Pohlmann's Diploma
+-- thesis.
+--
+-- @end
+
+
+
+---
+
+declare {
+ key = "sweep crossing minimization",
+ algorithm = require "pgf.gd.layered.CrossingMinimizationGansnerKNV1993",
+ phase = "crossing minimization",
+ phase_default = true,
+
+ summary = [["
+ Gansner et al. combine an initial ordering based on a depth-first
+ search with the median and greedy switch heuristics applied in the
+ form of an alternating layer-by-layer sweep based on a weighted
+ median.
+ "]],
+ documentation = [["
+ For more details, please see Section~4.1.4 of Pohlmann's Diploma
+ thesis.
+
+ This is the default algorithm for crossing minimization.
+ "]]
+}
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/layered/cycle_removal.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/layered/cycle_removal.lua
new file mode 100644
index 0000000000..18670df14e
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/layered/cycle_removal.lua
@@ -0,0 +1,136 @@
+-- Copyright 2012 by 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
+
+-- @release $Header$
+
+
+local declare = require("pgf.gd.interface.InterfaceToAlgorithms").declare
+
+
+---
+-- @section subsection {Cycle Removal}
+--
+-- The Sugiyama method works only on directed \emph{acyclic}
+-- graphs. For this reason, if the input graph is not (yet) acyclic, a
+-- number of edges need to be redirected so that acyclicity arises. In
+-- the following, the different options that allow you to fine-tune
+-- this process are documented.
+--
+-- @end
+
+
+
+---
+
+declare {
+ key = "depth first cycle removal",
+ algorithm = require "pgf.gd.layered.CycleRemovalGansnerKNV1993",
+ phase = "cycle removal",
+ phase_default = true,
+
+ summary = [["
+ Selects a cycle removal algorithm that is especially
+ appropriate for graphs specified ``by hand''.
+ "]],
+ documentation = [["
+ When graphs are created by humans manually, one can
+ make assumptions about the input graph that would otherwise not
+ be possible. For instance, it seems reasonable to assume that the
+ order in which nodes and edges are entered by the user somehow
+ reflects the natural flow the user has had in mind for the graph.
+
+ In order to preserve the natural flow of the input graph, Gansner
+ et al.\ propose to remove cycles by performing a series of
+ depth-first searches starting at individual nodes in the order they
+ appear in the graph. This algorithm implicitly constructs a spanning
+ tree of the nodes reached during the searches. It thereby partitions
+ the edges of the graph into tree edges and non-tree edges. The
+ non-tree edges are further subdivided into forward edges, cross edges,
+ and back edges. Forward edges point from a tree nodes to one of their
+ descendants. Cross edges connect unrelated branches in the search tree.
+ Back edges connect descendants to one of their ancestors. It is not
+ hard to see that reversing back edges will not only introduce no new
+ cycles but will also make any directed graph acyclic.
+ Gansner et al.\ argue that this approach is more stable than others
+ in that fewer inappropriate edges are reversed compared to other
+ methods, despite the lack of a provable upper bound for the number
+ of reversed edges.
+
+ See section~4.1.1 of Pohlmann's Diplom thesis for more details.
+
+ This is the default algorithm for cycle removals.
+ "]]
+ }
+
+---
+
+declare {
+ key = "prioritized greedy cycle removal",
+ algorithm = "pgf.gd.layered.CycleRemovalEadesLS1993",
+ phase = "cycle removal",
+
+ summary = [["
+ This algorithm implements a greedy heuristic of Eades et al.\ for
+ cycle removal that prioritizes sources and sinks.
+ "]],
+ documentation = [["
+ See section~4.1.1 of Pohlmann's Diploma theses for details.
+ "]]
+}
+
+
+---
+
+declare {
+ key = "greedy cycle removal",
+ algorithm = "pgf.gd.layered.CycleRemovalEadesLS1993",
+ phase = "cycle removal",
+
+ summary = [["
+ This algorithm implements a greedy heuristic of Eades et al.\ for
+ cycle removal that prioritizes sources and sinks.
+ "]],
+ documentation = [["
+ See section~4.1.1 of Pohlmann's Diploma theses for details.
+ "]]
+ }
+
+---
+
+declare {
+ key = "naive greedy cycle removal",
+ algorithm = "pgf.gd.layered.CycleRemovalBergerS1990a",
+ phase = "cycle removal",
+
+ summary = [["
+ This algorithm implements a greedy heuristic of Berger and Shor for
+ cycle removal. It is not really compared to the other heuristics and
+ only included for demonstration purposes.
+ "]],
+ documentation = [["
+ See section~4.1.1 of Pohlmann's Diploma theses for details.
+ "]]
+ }
+
+---
+
+declare {
+ key = "random greedy cycle removal",
+ algorithm = "pgf.gd.layered.CycleRemovalBergerS1990b",
+ phase = "cycle removal",
+
+ summary = [["
+ This algorithm implements a randomized greedy heuristic of Berger
+ and Shor for cycle removal. It, too, is not really compared to
+ the other heuristics and only included for demonstration purposes.
+ "]],
+ documentation = [["
+ See section~4.1.1 of Pohlmann's Diploma theses for details.
+ "]]
+ } \ No newline at end of file
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/layered/edge_routing.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/layered/edge_routing.lua
new file mode 100644
index 0000000000..eb9939c74c
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/layered/edge_routing.lua
@@ -0,0 +1,49 @@
+-- Copyright 2012 by 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
+
+-- @release $Header$
+
+
+local declare = require("pgf.gd.interface.InterfaceToAlgorithms").declare
+
+
+---
+-- @section subsection {Edge Routing}
+--
+-- The original layered drawing method described by Eades and Sugiyama
+-- in does not include the routing or shaping of edges as a main
+-- step. This makes sense if all nodes have the same size and
+-- shape. In practical scenarios, however, this assumption often does
+-- not hold. In these cases, advanced techniques may have to be
+-- applied in order to avoid overlaps of nodes and edges.
+--
+-- For more details, please see Section~4.1.5 of Pohlmann's Diploma
+-- thesis.
+--
+-- @end
+
+
+
+---
+
+declare {
+ key = "polyline layer edge routing",
+ algorithm = require "pgf.gd.layered.EdgeRoutingGansnerKNV1993",
+ phase = "layer edge routing",
+ phase_default = true,
+
+ summary = [["
+ This edge routing algorithm uses polygonal lines to connect nodes.
+ "]],
+ documentation = [["
+ For more details, please see Section~4.1.5 of Pohlmann's Diploma thesis.
+
+ This is the default algorithm for edge routing.
+ "]]
+}
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/layered/library.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/layered/library.lua
new file mode 100644
index 0000000000..950e02ffa3
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/layered/library.lua
@@ -0,0 +1,94 @@
+-- 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$
+
+
+
+---
+-- A ``layered'' layout of a graph tries to arrange the nodes in
+-- consecutive horizontal layers (naturally, by rotating the graph, this
+-- can be changed in to vertical layers) such that edges tend to be only
+-- between nodes on adjacent layers. Trees, for instance, can always be
+-- laid out in this way. This method of laying out a graph is especially
+-- useful for hierarchical graphs.
+--
+-- The method implemented in this library is often called the
+-- \emph{Sugiyama method}, which is a rather advanced method of
+-- assigning nodes to layers and positions on these layers. The same
+-- method is also used in the popular GraphViz program, indeed, the
+-- implementation in \tikzname\ is based on the same pseudo-code from the
+-- same paper as the implementation used in GraphViz and both programs
+-- will often generate the same layout (but not always, as explained
+-- below). The current implementation is due to Jannis Pohlmann, who
+-- implemented it as part of his Diploma thesis. Please consult this
+-- thesis for a detailed explanation of the Sugiyama method and its
+-- history:
+-- %
+-- \begin{itemize}
+-- \item
+-- Jannis Pohlmann,
+-- \newblock \emph{Configurable Graph Drawing Algorithms
+-- for the \tikzname\ Graphics Description Language,}
+-- \newblock Diploma Thesis,
+-- \newblock Institute of Theoretical Computer Science, Universit\"at
+-- zu L\"ubeck, 2011.\\[.5em]
+-- \newblock Available online via
+-- \url{http://www.tcs.uni-luebeck.de/downloads/papers/2011/}\\
+-- \url{2011-configurable-graph-drawing-algorithms-jannis-pohlmann.pdf}
+-- \\[.5em]
+-- (Note that since the publication of this thesis some option names
+-- have been changed. Most noticeably, the option name
+-- |layered drawing| was changed to |layered layout|, which is somewhat
+-- more consistent with other names used in the graph drawing
+-- libraries. Furthermore, the keys for choosing individual
+-- algorithms for the different algorithm phases, have all changed.)
+-- \end{itemize}
+--
+-- The Sugiyama methods lays out a graph in five steps:
+-- %
+-- \begin{enumerate}
+-- \item Cycle removal.
+-- \item Layer assignment (sometimes called node ranking).
+-- \item Crossing minimization (also referred to as node ordering).
+-- \item Node positioning (or coordinate assignment).
+-- \item Edge routing.
+-- \end{enumerate}
+-- %
+-- It turns out that behind each of these steps there lurks an
+-- NP-complete problem, which means, in practice, that each step is
+-- impossible to perform optimally for larger graphs. For this reason,
+-- heuristics and approximation algorithms are used to find a ``good''
+-- way of performing the steps.
+--
+-- A distinctive feature of Pohlmann's implementation of the Sugiyama
+-- method for \tikzname\ is that the algorithms used for each of the
+-- steps can easily be exchanged, just specify a different option. For
+-- the user, this means that by specifying a different option and thereby
+-- using a different heuristic for one of the steps, a better layout can
+-- often be found. For the researcher, this means that one can very
+-- easily test new approaches and new heuristics without having to
+-- implement all of the other steps anew.
+--
+-- @library
+
+local layered
+
+
+-- Load declarations from:
+require "pgf.gd.layered"
+
+-- Load algorithms from:
+require "pgf.gd.layered.Sugiyama"
+require "pgf.gd.layered.cycle_removal"
+require "pgf.gd.layered.node_ranking"
+require "pgf.gd.layered.crossing_minimization"
+require "pgf.gd.layered.node_positioning"
+require "pgf.gd.layered.edge_routing"
+
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/layered/node_positioning.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/layered/node_positioning.lua
new file mode 100644
index 0000000000..e51382d642
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/layered/node_positioning.lua
@@ -0,0 +1,55 @@
+-- Copyright 2012 by 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
+
+-- @release $Header$
+
+
+local declare = require("pgf.gd.interface.InterfaceToAlgorithms").declare
+
+
+---
+-- @section subsection {Node Positioning (Coordinate Assignment)}
+--
+-- The second last step of the Sugiyama method decides about the final
+-- $x$- and $y$-coordinates of the nodes. The main objectives of this
+-- step are to position nodes so that the number of edge bends is kept
+-- small and edges are drawn as vertically as possible. Another goal
+-- is to avoid node and edge overlaps which is crucial in particular
+-- if the nodes are allowed to have non-uniform sizes. The
+-- $y$-coordinates of the nodes have no influence on the number of
+-- bends. Obviously, nodes need to be separated enough geometrically
+-- so that they do not overlap. It feels natural to aim at separating
+-- all layers in the drawing by the same amount. Large nodes, however,
+-- may force node positioning algorithms to override this uniform
+-- level distance in order to avoid overlaps.
+--
+-- For more details, please see Section~4.1.2 of Pohlmann's Diploma thesis.
+--
+-- @end
+
+
+
+---
+
+declare {
+ key = "linear optimization node positioning",
+ algorithm = require "pgf.gd.layered.NodePositioningGansnerKNV1993",
+ phase = "node positioning",
+ phase_default = true,
+
+ summary = [["
+ This node positioning method, due to Gasner et al., is based on a
+ linear optimization problem.
+ "]],
+ documentation = [["
+ For more details, please see Section~4.1.3 of Pohlmann's Diploma thesis.
+
+ This is the default algorithm for layer assignments.
+ "]]
+}
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/layered/node_ranking.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/layered/node_ranking.lua
new file mode 100644
index 0000000000..c663d9ce36
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/layered/node_ranking.lua
@@ -0,0 +1,72 @@
+-- Copyright 2012 by 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
+
+-- @release $Header$
+
+
+local declare = require("pgf.gd.interface.InterfaceToAlgorithms").declare
+
+
+---
+-- @section subsection {Layer Assignment (Node Ranking)}
+--
+-- Algorithms for producing layered drawings place nodes on discrete
+-- layers from top to bottom. Layer assignment is the problem of
+-- finding a partition so that for all edges $e = (u,v) \in E(G)$ the
+-- equation $\mathit{layer}(u) < \mathit{layer}(v)$ holds. Such a
+-- partition is called a \emph{layering}. This definition can be extended by
+-- introducing edge weights or priorities and minimum length
+-- constraints which has practical applications and allows users to
+-- fine-tune the results.
+--
+-- For more details, please see Section~4.1.2 of Pohlmann's Diploma
+-- thesis.
+--
+-- @end
+
+
+
+---
+
+declare {
+ key = "linear optimization layer assignment",
+ algorithm = require "pgf.gd.layered.NodeRankingGansnerKNV1993",
+ phase = "node ranking",
+ phase_default = true,
+
+ summary = [["
+ This layer assignment method, due to Gasner et al., is based on a
+ linear optimization problem.
+ "]],
+ documentation = [["
+ For more details, please see Section~4.1.2 of Pohlmann's Diploma
+ thesis.
+
+ This is the default algorithm for layer assignments.
+ "]]
+}
+
+
+
+---
+
+declare {
+ key = "minimum height layer assignment",
+ algorithm = "pgf.gd.layered.NodeRankingMinimumHeight",
+ phase = "node ranking",
+
+ summary = [["
+ This layer assignment method minimizes the height of the resulting graph.
+ "]],
+ documentation = [["
+ For more details, please see Section~4.1.3 of Pohlmann's Diploma thesis.
+ "]]
+}
+
+
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/lib.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/lib.lua
new file mode 100644
index 0000000000..cc39ddd607
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/lib.lua
@@ -0,0 +1,435 @@
+-- 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$
+
+
+
+---
+-- Basic library functions
+
+local lib = {}
+
+-- Declare namespace
+
+require("pgf.gd").lib = lib
+
+
+-- General lib functions:
+
+
+---
+-- Finds the first value in the |array| for which |test| is true.
+--
+-- @param array An array to search in.
+-- @param test A function that is applied to each element of the
+-- array together with the index of the element and the
+-- whole table.
+--
+-- @return The value of the first value where the test is true.
+-- @return The index of the first value where the test is true.
+-- @return The function value of the first value where the test is
+-- true (only returned if test is a function).
+--
+function lib.find(array, test)
+ for i=1,#array do
+ local t = array[i]
+ local result = test(t,i,array)
+ if result then
+ return t,i,result
+ end
+ end
+end
+
+
+---
+-- Finds the first value in the |array| for which a function
+-- returns a minimal value
+--
+-- @param array An array to search in.
+-- @param f A function that is applied to each element of the
+-- array together with the index of the element and the
+-- whole table. It should return an integer and, possibly, a value.
+--
+-- Among all elements for which a non-nil integer is returned, let |i|
+-- by the index of the element where this integer is minimal.
+--
+-- @return |array[i]|
+-- @return |i|
+-- @return The return value(s) of the function at |array[i]|.
+--
+function lib.find_min(array, f)
+ local best = math.huge
+ local best_result
+ local best_index
+ for i=1,#array do
+ local t = array[i]
+ local result, p = f(t,i,array)
+ if result and p < best then
+ best = p
+ best_result = result
+ best_index = i
+ end
+ end
+ if best_index then
+ return array[best_index],best_index,best_result,best
+ end
+end
+
+
+
+
+---
+-- Copies a table while preserving its metatable.
+--
+-- @param source The table to copy.
+-- @param target The table to which values are to be copied or |nil| if a new
+-- table is to be allocated.
+--
+-- @return The |target| table or a newly allocated table containing all
+-- keys and values of the |source| table.
+--
+function lib.copy(source, target)
+ if not target then
+ target = {}
+ end
+ for key, val in pairs(source) do
+ target[key] = val
+ end
+ return setmetatable(target, getmetatable(source))
+end
+
+
+---
+-- Copies an array while preserving its metatable.
+--
+-- @param source The array to copy.
+-- @param target The array to which values are to be copied or |nil| if a new
+-- table is to be allocated. The elements of the
+-- |source| array will be added at the end.
+--
+-- @return The |target| table or a newly allocated table containing all
+-- keys and values of the |source| table.
+--
+function lib.icopy(source, target)
+ target = target or {}
+ for _, val in ipairs(source) do
+ target[#target+1] = val
+ end
+ return setmetatable(target, getmetatable(source))
+end
+
+
+
+
+---
+-- Apply a function to all pairs of a table, resulting in a new table.
+--
+-- @param source The table.
+-- @param fun A function taking two arguments (|val| and |key|, in
+-- that order). Should return two values (a |new_val| and a
+-- |new_key|). This pair will be inserted into the new table. If,
+-- however, |new_key| is |nil|, the |new_value| will be inserted at
+-- the position |key|. This means, in particular, that if the |fun|
+-- takes only a single argument and returns only a single argument,
+-- you have a ``classical'' value mapper. Also note that if
+-- |new_value| is |nil|, the value is removed from the table.
+--
+-- @return The new table.
+--
+function lib.map(source, fun)
+ local target = {}
+ for key, val in pairs(source) do
+ local new_val, new_key = fun(val, key)
+ if new_key == nil then
+ new_key = key
+ end
+ target[new_key] = new_val
+ end
+ return target
+end
+
+
+
+---
+-- Apply a function to all elements of an array, resulting in a new
+-- array.
+--
+-- @param source The array.
+-- @param fun A function taking two arguments (|val| and |i|, the
+-- current index). This function is applied to all elements of the
+-- array. The result of this function is placed at the end of a new
+-- array, expect when the function returns |nil|, in which case the
+-- element is skipped. If this function is not provided (is |nil|),
+-- the identity function is used.
+-- @param new The target array (if |nil|, a new array is create).
+-- %
+--\begin{codeexample}[code only]
+-- local a = lib.imap(array, function(v) if some_test(v) then return v end end)
+--\end{codeexample}
+--
+-- The above code is a filter that will remove all elements from the
+-- array that do not pass |some_test|.
+-- %
+--\begin{codeexample}[code only]
+-- lib.imap(a, lib.id, b)
+--\end{codeexample}
+--
+-- The above code has the same effect as |lib.icopy(a,b)|.
+--
+-- @return The new array
+--
+function lib.imap(source, fun, new)
+ if not new then
+ new = { }
+ end
+ for i, v in ipairs(source) do
+ new[#new+1] = fun(v, i)
+ end
+ return new
+end
+
+
+---
+-- Generate a random permutation of the numbers $1$ to $n$ in time
+-- $O(n)$. Knuth's shuffle is used for this.
+--
+-- @param n The desired size of the table
+-- @return A random permutation
+
+function lib.random_permutation(n)
+ local p = {}
+ for i=1,n do
+ p[i] = i
+ end
+ for i=1,n-1 do
+ local j = lib.random(i,n)
+ p[i], p[j] = p[i], p[j]
+ end
+ return p
+end
+
+
+---
+-- The identity function, so you can write |lib.id| instead of
+-- |function (x) return x end|.
+--
+
+function lib.id(...)
+ return ...
+end
+
+
+
+---
+-- Tries to find an option in different objects that have an
+-- options field.
+--
+-- This function iterates over all objects given as parameters. In
+-- each, it tries to find out whether the options field of the object
+-- contains the option |name| and, if so,
+-- returns the value. The important point is that checking whether the
+-- option table of an object contains the name field is done using
+-- |rawget| for all but the last parameter. This means that when you
+-- write
+-- %
+--\begin{codeexample}[code only]
+--lib.lookup_option("foo", vertex, graph)
+--\end{codeexample}
+-- %
+-- and if |/graph drawing/foo| has an initial value set, if the
+-- parameter is not explicitly set in a vertex, you will get the value
+-- set for the graph or, if it is not set there either, the initial
+-- value. In contrast, if you write
+-- %
+--\begin{codeexample}[code only]
+-- vertex.options["foo"] or graph.options["foo"]
+--\end{codeexample}
+-- %
+-- what happens is that the first access to |.options| will
+-- \emph{always} return something when an initial parameter has been
+-- set for the option |foo|.
+--
+-- @param name The name of the options
+-- @param ... Any number of objects. Each must have an options
+-- field.
+--
+-- @return The found option
+
+function lib.lookup_option(name, ...)
+ local list = {...}
+ for i=1,#list-1 do
+ local o = list[i].options
+ if o then
+ local v = rawget(o, name)
+ if v then
+ return v
+ end
+ end
+ end
+ return list[#list].options[name]
+end
+
+
+
+---
+-- Turns a table |t| into a class in the sense of object oriented
+-- programming. In detail, this means that |t| is augmented by
+-- a |new| function, which takes an optional table of |initial| values
+-- and which outputs a new table whose metatable is the
+-- class. The |new| function will call the function |constructor| if
+-- it exists. Furthermore, the class object's |__index| is set to itself
+-- and its meta table is set to the |base_class| field of the
+-- table. If |t| is |nil|, a new table is created.
+--
+-- Here is a typical usage of this function:
+-- %
+--\begin{codeexample}[code only]
+--local Point = lib.class {}
+--
+--function Point:length()
+-- return math.sqrt(self.x*self.x + self.y*self.y)
+--end
+--
+--local p = Point.new { x = 5, y = 6 }
+--
+--print(p:length())
+--\end{codeexample}
+-- %
+-- We can subclass this as follows:
+-- %
+--\begin{codeexample}[code only]
+--local Point3D = lib.class { base_class = Point }
+--
+--function Point3D:length()
+-- local l = Point.length(self) -- Call base class's function
+-- return math.sqrt(l*l + self.z*self.zdy)
+--end
+--
+--local p = Point3D.new { x = 5, y = 6, z = 6 }
+--
+--print(p:length())
+--\end{codeexample}
+--
+-- @param t A table that gets augmented to a class. If |nil|, a new
+-- table is created.
+-- @return The augmented table.
+
+function lib.class(t)
+ t = t or {}
+
+ -- First, setup indexing, if necessary
+ if not t.__index then
+ t.__index = t
+ end
+
+ -- Second, setup new method, if necessary
+ t.new = t.new or
+ function (initial)
+
+ -- Create new object
+ local obj = {}
+ for k,v in pairs(initial or {}) do
+ obj[k] = v
+ end
+ setmetatable(obj, t)
+
+ if obj.constructor then
+ obj:constructor()
+ end
+
+ return obj
+ end
+
+ -- Third, setup inheritance, if necessary
+ if not getmetatable(t) then
+ setmetatable(t, t.base_class)
+ end
+
+ return 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
+
+
+
+---
+-- This implements the a random number generator similar to the one
+-- provided by Lua, but based on the tex.uniformdeviate primitive to
+-- avoid differences in random numbers due to platform specifics.
+--
+-- @param l Lower bound
+-- @param u Upper bound
+-- @return A random number
+function lib.random(l,u)
+ local fraction_one = 268435456
+ local r = tex.uniform_rand(fraction_one)/fraction_one
+ if l and u then
+ assert(l <= u)
+ return math.floor(r*(u-l+1)) + l
+ elseif l then
+ assert(1.0 <= l)
+ return math.floor(r*l) + 1.0
+ else
+ return r
+ end
+end
+
+---
+-- Provide the seed for the random number generator
+--
+-- @param seed random seed
+function lib.randomseed(seed)
+ tex.init_rand(seed)
+end
+
+-- Done
+
+return lib
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/lib/Bezier.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/lib/Bezier.lua
new file mode 100644
index 0000000000..e4fe21d942
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/lib/Bezier.lua
@@ -0,0 +1,160 @@
+-- Copyright 2013 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$
+
+
+
+---
+-- This library offers a number of methods for working with Bezi\'er
+-- curves.
+
+local Bezier = {}
+
+-- Namespace
+require("pgf.gd.lib").Bezier = Bezier
+
+
+-- Imports
+
+local Coordinate = require 'pgf.gd.model.Coordinate'
+
+
+---
+-- Compute a point ``along a curve at a time''. You provide the four
+-- coordinates of the curve and a time. You get a point on the curve
+-- as return value as well as the two support vector for curve
+-- before this point and two support vectors for the curve after the
+-- point.
+--
+-- For speed reasons and in order to avoid superfluous creation of
+-- lots of tables, all values are provided and returned as pairs of
+-- values rather than as |Coordinate| objects.
+--
+-- @param ax The coordinate where the curve starts.
+-- @param ay
+-- @param bx The first support point.
+-- @param by
+-- @param cx The second support point.
+-- @param cy
+-- @param dx The coordinate where the curve ends.
+-- @param dy
+-- @param t A time (a number).
+--
+-- @return The point |p| on the curve at time |t| ($x$-part).
+-- @return The point |p| on the curve at time |t| ($y$-part).
+-- @return The first support point of the curve between |a| and |p| ($x$-part).
+-- @return The first support point of the curve between |a| and |p| ($y$-part).
+-- @return The second support point of the curve between |a| and |p| ($x$-part).
+-- @return The second support point of the curve between |a| and |p| ($y$-part).
+-- @return The first support point of the curve between |p| and |d| ($x$-part).
+-- @return The first support point of the curve between |p| and |d| ($y$-part).
+-- @return The second support point of the curve between |p| and |d| ($x$-part).
+-- @return The second support point of the curve between |p| and |d| ($y$-part).
+
+function Bezier.atTime(ax,ay,bx,by,cx,cy,dx,dy,t)
+
+ local s = 1-t
+
+ local ex, ey = ax*s + bx*t, ay*s + by*t
+ local fx, fy = bx*s + cx*t, by*s + cy*t
+ local gx, gy = cx*s + dx*t, cy*s + dy*t
+
+ local hx, hy = ex*s + fx*t, ey*s + fy*t
+ local ix, iy = fx*s + gx*t, fy*s + gy*t
+
+ local jx, jy = hx*s + ix*t, hy*s + iy*t
+
+ return jx, jy, ex, ey, hx, hy, ix, iy, gx, gy
+end
+
+
+---
+-- The ``coordinate version'' of the |atTime| function, where both the
+-- parameters and the return values are coordinate objects.
+
+function Bezier.atTimeCoordinates(a,b,c,d,t)
+ local jx, jy, ex, ey, hx, hy, ix, iy, gx, gy =
+ Bezier.atTime(a.x,a.y,b.x,b.y,c.x,c.y,d.x,d.y,t)
+
+ return
+ Coordinate.new(jx, jy),
+ Coordinate.new(ex, ey),
+ Coordinate.new(hx, hy),
+ Coordinate.new(ix, iy),
+ Coordinate.new(gx, gy)
+end
+
+
+---
+-- Computes the support points of a Bezier curve based on two points
+-- on the curves at certain times.
+--
+-- @param from The start point of the curve
+-- @param p1 A first point on the curve
+-- @param t1 A time when this point should be reached
+-- @param p2 A second point of the curve
+-- @param t2 A time when this second point should be reached
+-- @param to The end of the curve
+--
+-- @return sup1 A first support point of the curve
+-- @return sup2 A second support point of the curve
+
+function Bezier.supportsForPointsAtTime(from, p1, t1, p2, t2, to)
+
+ local s1 = 1 - t1
+ local s2 = 1 - t2
+
+ local f1a = s1^3
+ local f1b = t1 * s1^2 * 3
+ local f1c = t1^2 * s1 * 3
+ local f1d = t1^3
+
+ local f2a = s2^3
+ local f2b = t2 * s2^2 * 3
+ local f2c = t2^2 * s2 * 3
+ local f2d = t2^3
+
+ -- The system:
+ -- p1.x - from.x * f1a - to.x * f1d = sup1.x * f1b + sup2.x * f1c
+ -- p2.x - from.x * f2a - to.x * f2d = sup1.x * f2b + sup2.x * f2c
+ --
+ -- p1.y - from.y * f1a - to.y * f1d = sup1.y * f1b + sup2.y * f1c
+ -- p2.y - from.y * f2a - to.y * f2d = sup1.y * f2b + sup2.y * f2c
+
+ local a = f1b
+ local b = f1c
+ local c = p1.x - from.x * f1a - to.x * f1d
+ local d = f2b
+ local e = f2c
+ local f = p2.x - from.x * f2a - to.x * f2d
+
+ local det = a*e - b*d
+ local x1 = -(b*f - e*c)/det
+ local x2 = -(c*d - a*f)/det
+
+ local c = p1.y - from.y * f1a - to.y * f1d
+ local f = p2.y - from.y * f2a - to.y * f2d
+
+ local det = a*e - b*d
+ local y1 = -(b*f - e*c)/det
+ local y2 = -(c*d - a*f)/det
+
+ return Coordinate.new(x1,y1), Coordinate.new(x2,y2)
+
+end
+
+
+
+
+
+
+-- Done
+
+return Bezier \ No newline at end of file
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/lib/DepthFirstSearch.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/lib/DepthFirstSearch.lua
new file mode 100644
index 0000000000..564b8f6839
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/lib/DepthFirstSearch.lua
@@ -0,0 +1,125 @@
+-- Copyright 2011 by Jannis Pohlmann
+-- 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$
+
+
+
+--- The DepthFirstSearch class implements a generic depth first function. It does not
+-- require that it is run on graphs, but can be used for anything where a visit function and
+-- a complete function is available.
+
+local DepthFirstSearch = {}
+DepthFirstSearch.__index = DepthFirstSearch
+
+-- Namespace
+require("pgf.gd.lib").DepthFirstSearch = DepthFirstSearch
+
+-- Imports
+local Stack = require "pgf.gd.lib.Stack"
+
+
+
+-- TT: TODO Jannis: Please document...
+
+function DepthFirstSearch.new(init_func, visit_func, complete_func)
+ local dfs = {
+ init_func = init_func,
+ visit_func = visit_func,
+ complete_func = complete_func,
+
+ stack = Stack.new(),
+ discovered = {},
+ visited = {},
+ completed = {},
+ }
+ setmetatable(dfs, DepthFirstSearch)
+ return dfs
+end
+
+
+
+function DepthFirstSearch:run()
+ self:reset()
+ self.init_func(self)
+
+ while self.stack:getSize() > 0 do
+ local data = self.stack:peek()
+
+ if not self:getVisited(data) then
+ if self.visit_func then
+ self.visit_func(self, data)
+ end
+ else
+ if self.complete_func then
+ self.complete_func(self, data)
+ end
+ self:setCompleted(data, true)
+ self.stack:pop()
+ end
+ end
+end
+
+
+
+function DepthFirstSearch:reset()
+ self.discovered = {}
+ self.visited = {}
+ self.completed = {}
+ self.stack = Stack.new()
+end
+
+
+
+function DepthFirstSearch:setDiscovered(data, discovered)
+ self.discovered[data] = discovered
+end
+
+
+
+function DepthFirstSearch:getDiscovered(data)
+ return self.discovered[data]
+end
+
+
+
+function DepthFirstSearch:setVisited(data, visited)
+ self.visited[data] = visited
+end
+
+
+
+function DepthFirstSearch:getVisited(data)
+ return self.visited[data]
+end
+
+
+
+function DepthFirstSearch:setCompleted(data, completed)
+ self.completed[data] = completed
+end
+
+
+
+function DepthFirstSearch:getCompleted(data)
+ return self.completed[data]
+end
+
+
+
+function DepthFirstSearch:push(data)
+ self.stack:push(data)
+end
+
+
+
+-- Done
+
+return DepthFirstSearch \ No newline at end of file
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/lib/Direct.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/lib/Direct.lua
new file mode 100644
index 0000000000..81728a1a44
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/lib/Direct.lua
@@ -0,0 +1,95 @@
+-- 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$
+
+
+
+--- Direct is a class that collects algorithms for computing new
+-- versions of a graph where arcs point in certain directions.
+
+local Direct = {}
+
+-- Namespace
+require("pgf.gd.lib").Direct = Direct
+
+-- Imports
+local Digraph = require "pgf.gd.model.Digraph"
+
+
+--- Compute a digraph from a syntactic digraph.
+--
+-- This function takes a syntactic digraph and compute a new digraph
+-- where all arrow point in the "semantic direction" of the syntactic
+-- arrows. For instance, while "a <- b" will cause an arc from a to be
+-- to be added to the syntactic digraph, calling this function will
+-- return a digraph in which there is an arc from b to a rather than
+-- the other way round. In detail, "a <- b" is translated as just
+-- described, "a -> b" yields an arc from a to b as expected, "a <-> b"
+-- and "a -- b" yield arcs in both directions and, finally, "a -!- b"
+-- yields no arc at all.
+--
+-- @param syntactic_digraph A syntactic digraph, usually the "input"
+-- graph as specified syntactically be the user.
+--
+-- @return A new "semantic" digraph object.
+
+function Direct.digraphFromSyntacticDigraph(syntactic_digraph)
+ local digraph = Digraph.new(syntactic_digraph) -- copy
+
+ -- Now go over all arcs of the syntactic_digraph and turn them into
+ -- arcs with the correct direction in the digraph:
+ for _,a in ipairs(syntactic_digraph.arcs) do
+ for _,m in ipairs(a.syntactic_edges) do
+ local direction = m.direction
+ if direction == "->" then
+ digraph:connect(a.tail, a.head)
+ elseif direction == "<-" then
+ digraph:connect(a.head, a.tail)
+ elseif direction == "--" or direction == "<->" then
+ digraph:connect(a.tail, a.head)
+ digraph:connect(a.head, a.tail)
+ end
+ -- Case -!-: No edges...
+ end
+ end
+
+ return digraph
+end
+
+
+--- Turn an arbitrary graph into a directed graph
+--
+-- Takes a digraph as input and returns its underlying undirected
+-- graph, coded as a digraph. This means that between any two vertices
+-- if there is an arc in one direction, there is also one in the other.
+--
+-- @param digraph A directed graph
+--
+-- @return The underlying undirected graph of digraph.
+
+function Direct.ugraphFromDigraph(digraph)
+ local ugraph = Digraph.new(digraph)
+
+ -- Now go over all arcs of the syntactic_digraph and turn them into
+ -- arcs with the correct direction in the digraph:
+ for _,a in ipairs(digraph.arcs) do
+ ugraph:connect(a.head,a.tail)
+ ugraph:connect(a.tail,a.head)
+ end
+
+ return ugraph
+end
+
+
+
+
+-- Done
+
+return Direct
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/lib/Event.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/lib/Event.lua
new file mode 100644
index 0000000000..880796ce97
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/lib/Event.lua
@@ -0,0 +1,98 @@
+-- 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$
+
+
+
+---
+-- Events are used to communicate ``interesting'' events from the
+-- parser to the graph drawing algorithms.
+--
+-- As a syntactic description of some graph is being parsed, vertices,
+-- arcs, and a digraph object representing this graph get
+-- constructed. However, even though syntactic annotations such as
+-- options for the vertices and arcs are attached to them and can be
+-- accessed through the graph objects, some syntactic information is
+-- neither represented in the digraph object nor in the vertices and
+-- the arcs. A typical example is a ``missing'' node in a tree: Since
+-- it is missing, there is neither a vertex object nor arc objects
+-- representing it. It is also not a global option of the graph.
+--
+-- For these reasons, in addition to the digraph object itself,
+-- additional information can be passed by a parser to graph drawing
+-- algorithms through the means of events. Each |Event| consists of a
+-- |kind| field, which is just some string, and a |parameters| field,
+-- which stores additional, kind-specific information. As a graph is
+-- being parsed, a string of events is accumulated and is later on
+-- available through the |events| field of the graph drawing scope.
+--
+-- The following events are created during the parsing process by the
+-- standard parsers of \tikzname:
+-- %
+-- \begin{itemize}
+-- \item[|node|] When a node of the input graph has been parsed and
+-- a |Vertex| object has been created for it, an event with kind
+-- |node| is created. The |parameter| of this event is the
+-- just-created vertex.
+--
+-- The same kind of event is used to indicate ``missing'' nodes. In
+-- this case, the |parameters| field is |nil|.
+-- \item[|edge|] When an edge of the input graph has been parsed, an
+-- event is created of kind |edge|. The |parameters| field will store
+-- an array with two entries: The first is the |Arc| object whose
+-- |syntactic_edges| field stores the |edge|. The second is the index
+-- of the edge inside the |syntactic_edges| field.
+-- \item[|begin|]
+-- Signals the beginning of a group, which will be ended with a
+-- corresponding |end| event later on. The |parameters| field will
+-- indicate the kind of group. Currently, only the string
+-- |"descendants"| is used as |parameters|, indicating the start of
+-- several nodes that are descendants of a given node. This
+-- information can be used by algorithms for reconstructing the
+-- input structure of trees.
+-- \item[|end|] Signals the end of a group begun by a |begin| event
+-- earlier on.
+-- \end{itemize}
+--
+-- @field kind A string representing the kind of the events.
+-- @field parameters Kind-specific parameters.
+-- @field index A number that stores the events logical position in
+-- the sequence of events. The number need not be an integer array
+-- index.
+--
+local Event = {}
+Event.__index = Event
+
+
+-- Namespace
+require("pgf.gd.lib").Event = Event
+
+
+
+---
+-- Create a new event object
+--
+-- @param initial Initial fields of the new event.
+--
+-- @return The new object
+
+function Event.new(values)
+ local new = {}
+ for k,v in pairs(values) do
+ new[k] = v
+ end
+ return setmetatable(new, Event)
+end
+
+
+
+-- done
+
+return Event \ No newline at end of file
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/lib/LookupTable.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/lib/LookupTable.lua
new file mode 100644
index 0000000000..fc2043d523
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/lib/LookupTable.lua
@@ -0,0 +1,111 @@
+-- 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$
+
+
+
+---
+-- This table provides two utility functions for managing ``lookup
+-- tables''. Such a table is a mixture of an array and a hashtable:
+-- It stores (only) tables. Each table is stored once in a normal
+-- array position. Additionally, the lookup table is also indexed at
+-- the position of the table (used as a key) and this position is set
+-- to |true|. This means that you can test whether a table |t| is in the
+-- lookup table |l| simply by testing whether |l[t]| is true.
+--
+local LookupTable = {}
+
+-- Namespace
+require("pgf.gd.lib").LookupTable = LookupTable
+
+
+
+---
+-- Add all elements in the |array| to a lookup table. If an element of
+-- the array is already present in the table, it will not be added
+-- again.
+--
+-- This operation takes time $O(|\verb!array!|)$.
+--
+-- @param l Lookup table
+-- @param array An array of to-be-added tables.
+
+function LookupTable.add(l, array)
+ for i=1,#array do
+ local t = array[i]
+ if not l[t] then
+ l[t] = true
+ l[#l + 1] = t
+ end
+ end
+end
+
+
+---
+-- Add one element to a lookup table. If it is already present in the
+-- table, it will not be added again.
+--
+-- This operation takes time $O(1)$.
+--
+-- @param l Lookup table
+-- @param e The to-be-added element.
+
+function LookupTable.addOne(l, e)
+ if not l[e] then
+ l[e] = true
+ l[#l + 1] = e
+ end
+end
+
+
+---
+-- Remove tables from a lookup table.
+--
+-- Note that this operation is pretty expensive insofar as it will
+-- always cost a traversal of the whole lookup table. However, this is
+-- also the maximum cost, even when a lot of entries need to be
+-- deleted. Thus, it is much better to ``pool'' multiple remove
+-- operations in a single one.
+--
+-- This operation takes time $O(\max\{|\verb!array!|, |\verb!l!|\})$.
+--
+-- @param l Lookup table
+-- @param t An array of to-be-removed tables.
+
+function LookupTable.remove(l, array)
+ -- Step 1: Mark all to-be-deleted entries
+ for i=1,#array do
+ local t = array[i]
+ if l[t] then
+ l[t] = false
+ end
+ end
+
+ -- Step 2: Collect garbage...
+ local target = 1
+ for i=1,#l do
+ local t = l[i]
+ if l[t] == false then
+ l[t] = nil
+ else
+ l[target] = t
+ target = target + 1
+ end
+ end
+ for i=#l,target,-1 do
+ l[i] = nil
+ end
+end
+
+
+
+-- Done
+
+return LookupTable \ No newline at end of file
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/lib/PathLengths.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/lib/PathLengths.lua
new file mode 100644
index 0000000000..4bfa896ef9
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/lib/PathLengths.lua
@@ -0,0 +1,209 @@
+-- Copyright 2011 by Jannis Pohlmann
+-- 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$
+
+
+
+---
+-- This table provides algorithms for computing distances between
+-- nodes of a graph (in the sense of path lengths).
+
+local PathLengths = {}
+
+-- Namespace
+require("pgf.gd.lib").PathLengths = PathLengths
+
+-- Import
+local PriorityQueue = require "pgf.gd.lib.PriorityQueue"
+
+
+
+---
+-- Performs the Dijkstra algorithm to solve the single-source shortest path problem.
+--
+-- The algorithm computes the shortest paths from |source| to all nodes
+-- in the graph. It also generates a table with distance level sets, each of
+-- which contain all nodes that have the same corresponding distance to
+-- |source|. Finally, a mapping of nodes to their parents along the
+-- shortest paths is generated to allow the reconstruction of the paths
+-- that were chosen by the Dijkstra algorithm.
+--
+-- @param graph The graph to compute the shortest paths for.
+-- @param source The node to compute the distances to.
+--
+-- @return A mapping of nodes to their distance to |source|.
+-- @return An array of distance level sets. The set at index |i| contains
+-- all nodes that have a distance of |i| to |source|.
+-- @return A mapping of nodes to their parents to allow the reconstruction
+-- of the shortest paths chosen by the Dijkstra algorithm.
+--
+function PathLengths.dijkstra(graph, 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 _,node in ipairs(graph.nodes) do
+ if node == source then
+ distance[node] = 0
+ parent[node] = nil
+ queue:enqueue(node, distance[node])
+ else
+ distance[node] = #graph.nodes + 1 -- this is about infinity ;)
+ queue:enqueue(node, distance[node])
+ end
+ end
+
+ while not queue:isEmpty() do
+ local u = queue:dequeue()
+
+ assert(distance[u] < #graph.nodes + 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(u.edges) do
+ local v = edge:getNeighbour(u)
+ 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
+
+
+
+
+---
+-- Performs the Floyd-Warshall algorithm to solve the all-source shortest path problem.
+--
+-- @param graph The graph to compute the shortest paths for.
+--
+-- @return A distance matrix
+--
+function PathLengths.floydWarshall(graph)
+ local distance = {}
+ local infinity = math.huge
+
+ for _,i in ipairs(graph.nodes) do
+ distance[i] = {}
+ for _,j in ipairs(graph.nodes) do
+ distance[i][j] = infinity
+ end
+ end
+
+ for _,i in ipairs(graph.nodes) do
+ for _,edge in ipairs(i.edges) do
+ local j = edge:getNeighbour(i)
+ distance[i][j] = edge.weight or 1
+ end
+ end
+
+ for _,k in ipairs(graph.nodes) do
+ for _,i in ipairs(graph.nodes) do
+ for _,j in ipairs(graph.nodes) do
+ distance[i][j] = math.min(distance[i][j], distance[i][k] + distance[k][j])
+ end
+ end
+ end
+
+ return distance
+end
+
+
+
+
+---
+-- Computes the pseudo diameter of a graph.
+--
+-- The diameter of a graph is the maximum of the shortest paths between
+-- any pair of nodes in the graph. A pseudo diameter is an approximation
+-- of the diameter that is computed by picking a starting node |u| and
+-- finding a node |v| that is farthest away from |u| and has the smallest
+-- degree of all nodes that have the same distance to |u|. The algorithm
+-- continues with |v| as the new starting node and iteratively tries
+-- to find an end node that is generates a larger pseudo diameter.
+-- It terminates as soon as no such end node can be found.
+--
+-- @param graph The graph.
+--
+-- @return The pseudo diameter of the graph.
+-- @return The start node of the corresponding approximation of a maximum
+-- shortest path.
+-- @return The end node of that path.
+--
+function PathLengths.pseudoDiameter(graph)
+
+ -- find a node with minimum degree
+ local start_node = graph.nodes[1]
+ for _,node in ipairs(graph.nodes) do
+ if node:getDegree() < start_node:getDegree() then
+ start_node = node
+ end
+ end
+
+ assert(start_node)
+
+ local old_diameter = 0
+ local diameter = 0
+ local end_node = nil
+
+ while true do
+ local distance, levels = PathLengths.dijkstra(graph, 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 node:getDegree() < start_node:getDegree() then
+ start_node = node
+ end
+ end
+
+ assert(start_node)
+ end
+
+ assert(start_node)
+ assert(end_node)
+
+ return diameter, start_node, end_node
+end
+
+
+
+
+
+-- Done
+
+return PathLengths \ No newline at end of file
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/lib/PriorityQueue.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/lib/PriorityQueue.lua
new file mode 100644
index 0000000000..3fd29cdb74
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/lib/PriorityQueue.lua
@@ -0,0 +1,342 @@
+-- Copyright 2011 by Jannis Pohlmann
+-- 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$
+
+
+
+---
+-- A PriorityQueue supports operations for quickly finding the minimum from a set of elements
+--
+-- Its implementation is based on (simplified) Fibonacci heaps.
+local PriorityQueue = {}
+PriorityQueue.__index = PriorityQueue
+
+
+-- Namespace
+local lib = require "pgf.gd.lib"
+lib.PriorityQueue = PriorityQueue
+
+
+
+-- Local declarations
+local FibonacciHeap = {}
+local FibonacciHeapNode = {}
+
+
+
+
+--- Creates a new priority queue
+--
+-- @return The newly created queue
+
+function PriorityQueue.new()
+ local queue = {
+ heap = FibonacciHeap.new(),
+ nodes = {},
+ values = {},
+ }
+ setmetatable(queue, PriorityQueue)
+ return queue
+end
+
+
+
+--- Add an element with a certain priority to the queue
+--
+-- @param value An object
+-- @param priority Its priority
+
+function PriorityQueue:enqueue(value, priority)
+ local node = self.heap:insert(priority)
+ self.nodes[value] = node
+ self.values[node] = value
+end
+
+
+
+--- Removes the element with the minimum priority from the queue
+--
+-- @return The element with the minimum priority
+
+function PriorityQueue:dequeue()
+ local node = self.heap:extractMinimum()
+
+ if node then
+ local value = self.values[node]
+ self.nodes[value] = nil
+ self.values[node] = nil
+ return value
+ else
+ return nil
+ end
+end
+
+
+
+--- Lower the priority of an element of a queue
+--
+-- @param value An object
+-- @param priority A new priority, which must be lower than the old priority
+
+function PriorityQueue:updatePriority(value, priority)
+ local node = self.nodes[value]
+ assert(node, 'updating the priority of ' .. tostring(value) .. ' failed because it is not in the priority queue')
+ self.heap:updateValue(node, priority)
+end
+
+
+
+--- Tests, whether the queue is empty
+--
+-- @return True, if the queue is empty
+
+function PriorityQueue:isEmpty()
+ return #self.heap.trees == 0
+end
+
+
+
+
+
+
+-- Internals: An implementation of Fibonacci heaps.
+FibonacciHeap.__index = FibonacciHeap
+
+
+function FibonacciHeap.new()
+ local heap = {
+ trees = trees or {},
+ minimum = nil,
+ }
+ setmetatable(heap, FibonacciHeap)
+ return heap
+end
+
+
+
+function FibonacciHeap:insert(value)
+ local node = FibonacciHeapNode.new(value)
+ local heap = FibonacciHeap.new()
+ table.insert(heap.trees, node)
+ self:merge(heap)
+ return node
+end
+
+
+
+function FibonacciHeap:merge(other)
+ for _, tree in ipairs(other.trees) do
+ table.insert(self.trees, tree)
+ end
+ self:updateMinimum()
+end
+
+
+
+function FibonacciHeap:extractMinimum()
+ if self.minimum then
+ local minimum = self:removeTableElement(self.trees, self.minimum)
+
+ for _, child in ipairs(minimum.children) do
+ child.root = child
+ table.insert(self.trees, child)
+ end
+
+ local same_degrees_found = true
+ while same_degrees_found do
+ same_degrees_found = false
+
+ local degrees = {}
+
+ for _, root in ipairs(self.trees) do
+ local degree = root:getDegree()
+
+ if degrees[degree] then
+ if root.value < degrees[degree].value then
+ self:linkRoots(root, degrees[degree])
+ else
+ self:linkRoots(degrees[degree], root)
+ end
+
+ degrees[degree] = nil
+ same_degrees_found = true
+ break
+ else
+ degrees[degree] = root
+ end
+ end
+ end
+
+ self:updateMinimum()
+
+ return minimum
+ end
+end
+
+
+
+function FibonacciHeap:updateValue(node, value)
+ local old_value = node.value
+ local new_value = value
+
+ if new_value <= old_value then
+ self:decreaseValue(node, value)
+ else
+ assert(false, 'FibonacciHeap:increaseValue is not implemented yet')
+ end
+end
+
+
+
+function FibonacciHeap:decreaseValue(node, value)
+ assert(value <= node.value)
+
+ node.value = value
+
+ if node.value < node.parent.value then
+ local parent = node.parent
+ self:cutFromParent(node)
+
+ if not parent:isRoot() then
+ if parent.marked then
+ self:cutFromParent(parent)
+ else
+ parent.marked = true
+ end
+ end
+ end
+
+ if node.value < self.minimum.value then
+ self.minimum = node
+ end
+end
+
+
+
+function FibonacciHeap:delete(node)
+ self:decreaseValue(node, -math.huge)
+ self:extractMinimum()
+end
+
+
+
+function FibonacciHeap:linkRoots(root, child)
+ child.root = root
+ child.parent = root
+
+ child = self:removeTableElement(self.trees, child)
+ table.insert(root.children, child)
+
+ return root
+end
+
+
+
+function FibonacciHeap:cutFromParent(node)
+ local parent = node.parent
+
+ node.root = node
+ node.parent = node
+ node.marked = false
+
+ node = self:removeTableElement(parent.children, node)
+ table.insert(self.trees, node)
+end
+
+
+
+function FibonacciHeap:updateMinimum()
+ self.minimum = self.trees[1]
+
+ for _, root in ipairs(self.trees) do
+ if root.value < self.minimum.value then
+ self.minimum = root
+ end
+ end
+end
+
+
+
+function FibonacciHeap:removeTableElement(input_table, element)
+ for i = 1, #input_table do
+ if input_table[i] == element then
+ return table.remove(input_table, i)
+ end
+ end
+end
+
+
+
+
+-- Now come the nodes
+
+FibonacciHeapNode.__index = FibonacciHeapNode
+
+function FibonacciHeapNode.new(value, root, parent)
+ local node = {
+ value = value,
+ children = {},
+ marked = false,
+ root = nil,
+ parent = nil,
+ }
+ setmetatable(node, FibonacciHeapNode)
+
+ if root then
+ node.root = root
+ node.parent = parent
+ else
+ node.root = node
+ node.parent = node
+ end
+
+ return node
+end
+
+function FibonacciHeapNode:addChild(value)
+ local child = FibonacciHeapNode.new(value, self.root, self)
+ table.insert(self.children, child)
+end
+
+function FibonacciHeapNode:getDegree()
+ return #self.children
+end
+
+
+
+function FibonacciHeapNode:setRoot(root)
+ self.root = root
+
+ if root == self then
+ self.parent = root
+ end
+
+ if #self.children > 0 then
+ for _, child in ipairs(self.children) do
+ child.root = root
+ end
+ end
+end
+
+
+
+function FibonacciHeapNode:isRoot()
+ return self.root == self
+end
+
+
+
+
+
+
+-- done
+
+return PriorityQueue \ No newline at end of file
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/lib/Simplifiers.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/lib/Simplifiers.lua
new file mode 100644
index 0000000000..bcb7276190
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/lib/Simplifiers.lua
@@ -0,0 +1,279 @@
+-- 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$
+
+
+
+--- The Simplifiers class is a singleton object.
+-- Its methods allow implement methods for simplifying graphs, for instance
+-- for removing loops or multiedges or computing spanning trees.
+
+local Simplifiers = {}
+
+-- Namespace
+local lib = require "pgf.gd.lib"
+lib.Simplifiers = Simplifiers
+
+
+
+
+-- Imports
+
+local Edge = require "pgf.gd.deprecated.Edge"
+local Node = require "pgf.gd.deprecated.Node"
+
+
+
+
+
+--- Algorithm to classify edges of a DFS search tree.
+--
+-- TODO Jannis: document this algorithm as soon as it is completed and bug-free.
+-- TT: Replace this algorithm by something else, perhaps?
+--
+function Simplifiers:classifyEdges(graph)
+ local discovered = {}
+ local visited = {}
+ local recursed = {}
+ local completed = {}
+
+ local tree_and_forward_edges = {}
+ local cross_edges = {}
+ local back_edges = {}
+
+ local stack = {}
+
+ local function push(node)
+ table.insert(stack, node)
+ end
+
+ local function peek()
+ return stack[#stack]
+ end
+
+ local function pop()
+ return table.remove(stack)
+ end
+
+ local initial_nodes = graph.nodes
+
+ for i=#initial_nodes,1,-1 do
+ local node = initial_nodes[i]
+ push(node)
+ discovered[node] = true
+ end
+
+ while #stack > 0 do
+ local node = peek()
+ local edges_to_traverse = {}
+
+ visited[node] = true
+
+ if not recursed[node] then
+ recursed[node] = true
+
+ local out_edges = node:getOutgoingEdges()
+ for _,edge in ipairs(out_edges) do
+ local neighbour = edge:getNeighbour(node)
+
+ if not discovered[neighbour] then
+ table.insert(tree_and_forward_edges, edge)
+ table.insert(edges_to_traverse, edge)
+ else
+ if not completed[neighbour] then
+ if not visited[neighbour] then
+ table.insert(tree_and_forward_edges, edge)
+ table.insert(edges_to_traverse, edge)
+ else
+ table.insert(back_edges, edge)
+ end
+ else
+ table.insert(cross_edges, edge)
+ end
+ end
+ end
+
+ if #edges_to_traverse == 0 then
+ completed[node] = true
+ pop()
+ else
+ for i=#edges_to_traverse,1,-1 do
+ local neighbour = edges_to_traverse[i]:getNeighbour(node)
+ discovered[neighbour] = true
+ push(neighbour)
+ end
+ end
+ else
+ completed[node] = true
+ pop()
+ end
+ end
+
+ return tree_and_forward_edges, cross_edges, back_edges
+end
+
+
+
+
+
+--
+--
+-- Loops and Multiedges
+--
+--
+
+
+--- Remove all loops from a graph
+--
+-- This method will remove all loops from a graph.
+--
+-- @param algorithm An algorithm object
+
+function Simplifiers:removeLoopsOldModel(algorithm)
+ local graph = algorithm.graph
+ local loops = {}
+
+ for _,edge in ipairs(graph.edges) do
+ if edge:getHead() == edge:getTail() then
+ loops[#loops+1] = edge
+ end
+ end
+
+ for i=1,#loops do
+ graph:deleteEdge(loops[i])
+ end
+
+ graph[algorithm].loops = loops
+end
+
+
+
+--- Restore loops that were previously removed.
+--
+-- @param algorithm An algorithm object
+
+function Simplifiers:restoreLoopsOldModel(algorithm)
+ local graph = algorithm.graph
+
+ for _,edge in ipairs(graph[algorithm].loops) do
+ graph:addEdge(edge)
+ edge:getTail():addEdge(edge)
+ end
+
+ graph[algorithm].loops = nil
+end
+
+
+
+
+--- Remove all multiedges.
+--
+-- Every multiedge of the graph will be replaced by a single edge.
+--
+-- @param algorithm An algorithm object
+
+function Simplifiers:collapseMultiedgesOldModel(algorithm, collapse_action)
+ local graph = algorithm.graph
+ local collapsed_edges = {}
+ local node_processed = {}
+
+ for _,node in ipairs(graph.nodes) do
+ node_processed[node] = true
+
+ local multiedge = {}
+
+ local function handle_edge (edge)
+
+ local neighbour = edge:getNeighbour(node)
+
+ if not node_processed[neighbour] then
+ if not multiedge[neighbour] then
+ multiedge[neighbour] = Edge.new{ direction = Edge.RIGHT }
+ collapsed_edges[multiedge[neighbour]] = {}
+ end
+
+ if collapse_action then
+ collapse_action(multiedge[neighbour], edge, graph)
+ end
+
+ table.insert(collapsed_edges[multiedge[neighbour]], edge)
+ end
+ end
+
+ for _,edge in ipairs(node:getIncomingEdges()) do
+ handle_edge(edge)
+ end
+
+ for _,edge in ipairs(node:getOutgoingEdges()) do
+ handle_edge(edge)
+ end
+
+ for neighbour, multiedge in pairs(multiedge) do
+
+ if #collapsed_edges[multiedge] <= 1 then
+ collapsed_edges[multiedge] = nil
+ else
+ for _,subedge in ipairs(collapsed_edges[multiedge]) do
+ graph:deleteEdge(subedge)
+ end
+
+ multiedge:addNode(node)
+ multiedge:addNode(neighbour)
+
+ graph:addEdge(multiedge)
+ end
+ end
+ end
+
+ graph[algorithm].collapsed_edges = collapsed_edges
+end
+
+
+--- Expand multiedges that were previously collapsed
+--
+-- @param algorithm An algorithm object
+
+function Simplifiers:expandMultiedgesOldModel(algorithm)
+ local graph = algorithm.graph
+ for multiedge, subedges in pairs(graph[algorithm].collapsed_edges) do
+ assert(#subedges >= 2)
+
+ graph:deleteEdge(multiedge)
+
+ for _,edge in ipairs(subedges) do
+
+ -- Copy bend points
+ for _,p in ipairs(multiedge.bend_points) do
+ edge.bend_points[#edge.bend_points+1] = p:copy()
+ end
+
+ -- Copy options
+ for k,v in pairs(multiedge.algorithmically_generated_options) do
+ edge.algorithmically_generated_options[k] = v
+ end
+
+ for _,node in ipairs(edge.nodes) do
+ node:addEdge(edge)
+ end
+
+ graph:addEdge(edge)
+ end
+ end
+
+ graph[algorithm].collapsed_edges = nil
+end
+
+
+
+
+
+-- Done
+
+return Simplifiers
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/lib/Stack.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/lib/Stack.lua
new file mode 100644
index 0000000000..75084b6e38
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/lib/Stack.lua
@@ -0,0 +1,61 @@
+-- Copyright 2011 by Jannis Pohlmann
+-- 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$
+
+
+--- A Stack is a very simple wrapper around an array
+--
+--
+
+local Stack = {}
+Stack.__index = Stack
+
+
+-- Namespace
+require("pgf.gd.lib").Stack = Stack
+
+
+--- Create a new stack
+function Stack.new()
+ local stack = {}
+ setmetatable(stack, Stack)
+ return stack
+end
+
+
+--- Push an element on top of the stack
+function Stack:push(data)
+ self[#self+1] = data
+end
+
+
+--- Inspect (but not pop) the top element of a stack
+function Stack:peek()
+ return self[#self]
+end
+
+
+--- Pop an element from the top of the stack
+function Stack:pop()
+ return table.remove(self, #self)
+end
+
+
+--- Get the height of the stack
+function Stack:getSize()
+ return #self
+end
+
+
+
+-- done
+
+return Stack \ No newline at end of file
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/lib/Storage.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/lib/Storage.lua
new file mode 100644
index 0000000000..e029fdf7ff
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/lib/Storage.lua
@@ -0,0 +1,110 @@
+-- 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$
+
+
+
+---
+-- A storage is an object that, as the name suggests, allows you to
+-- ``store stuff concerning objects.'' Basically, it behaves like
+-- table having weak keys, which means that once the objects for which
+-- you ``store stuff'' go out of scope, they are also removed from the
+-- storage. Also, you can specify that for each object of the storage
+-- you store a table. In this case, there is no need to initialize
+-- this table for each object; rather, when you write into such a
+-- table and it does not yet exist, it is created ``on the fly''.
+--
+-- The typical way you use storages is best explained with the
+-- following example: Suppose you want to write a depth-first search
+-- algorithm for a graph. This algorithm might wish to mark all nodes
+-- it has visited. It could just say |v.marked = true|, but this might
+-- clash with someone else also using the |marked| key. The solution is
+-- to create a |marked| storage. The algorithm can first say
+--\begin{codeexample}[code only, tikz syntax=false]
+--local marked = Storage.new()
+--\end{codeexample}
+-- and then say
+--\begin{codeexample}[code only, tikz syntax=false]
+--marked[v] = true
+--\end{codeexample}
+-- to mark its objects. The |marked| storage object does not need to
+-- be created locally inside a function, you can declare it as a local
+-- variable of the whole file; nevertheless, the entries for vertices
+-- no longer in use get removed automatically. You can also make it a
+-- member variable of the algorithm class, which allows you make the
+-- information about which objects are marked globally
+-- accessible.
+--
+-- Now suppose the algorithm would like to store even more stuff in
+-- the storage. For this, we might use a table and can use the fact
+-- that a storage will automatically create a table when necessary:
+--\begin{codeexample}[code only, tikz syntax=false]
+--local info = Storage.newTableStorage()
+--
+--info[v].marked = true -- the "info[v]" table is
+-- -- created automatically here
+--
+--info[v].foo = "bar"
+--\end{codeexample}
+-- Again, once |v| goes out of scope, both it and the info table will
+-- removed.
+
+local Storage = {}
+
+-- Namespace
+require("pgf.gd.lib").Storage = Storage
+
+
+-- The simple metatable
+
+local SimpleStorageMetaTable = { __mode = "k" }
+
+-- The advanced metatable for table storages:
+
+local TableStorageMetaTable = {
+ __mode = "k",
+ __index =
+ function(t, k)
+ local new = {}
+ rawset(t, k, new)
+ return new
+ end
+}
+
+
+---
+-- Create a new storage object.
+--
+-- @return A new |Storage| instance.
+
+function Storage.new()
+ return setmetatable({}, SimpleStorageMetaTable)
+end
+
+
+---
+-- Create a new storage object which will install a table for every
+-- entry automatically.
+--
+-- @return A new |Storage| instance.
+
+function Storage.newTableStorage()
+ return setmetatable({}, TableStorageMetaTable)
+end
+
+
+
+
+
+
+
+-- Done
+
+return Storage
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/lib/Transform.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/lib/Transform.lua
new file mode 100644
index 0000000000..7695b1b239
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/lib/Transform.lua
@@ -0,0 +1,120 @@
+-- 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$
+
+
+---
+-- The |Transform| table provides a set of static methods for
+-- creating and handling canvas transformation matrices. Such a matrix
+-- is actually just an array of six numbers. The idea is that
+-- ``applying'' an array { a, b, c, d, e, f } a vector $(x,y)$ will
+-- yield the new vector $(ax+by+e,cx+dy+f)$. For details on how such
+-- matrices work, see Section~\ref{section-transform-cm}
+--
+local Transform = {}
+
+
+-- Namespace
+
+require("pgf.gd.model").Transform = Transform
+
+
+--- Creates a new transformation array.
+--
+-- @param a First component
+-- @param b Second component
+-- @param c Third component
+-- @param d Fourth component
+-- @param x The x shift
+-- @param y The y shift
+--
+-- @return A transformation object.
+--
+function Transform.new(a,b,c,d,x,y)
+ return { a, b, c, d, x, y }
+end
+
+
+--- Creates a new transformation object that represents a shift.
+--
+-- @param x An x-shift
+-- @param y A y-shift
+--
+-- @return A transformation object
+--
+function Transform.new_shift(x,y)
+ return { 1, 0, 0, 1, x, y }
+end
+
+
+--- Creates a new transformation object that represents a rotation.
+--
+-- @param angle An angle
+--
+-- @return A transformation object
+--
+function Transform.new_rotation(angle)
+ local c = math.cos(angle)
+ local s = math.sin(angle)
+ return { c, -s, s, c, 0, 0 }
+end
+
+
+--- Creates a new transformation object that represents a scaling.
+--
+-- @param x The horizontal scaling
+-- @param y The vertical scaling (if missing, the horizontal scaling is used)
+--
+-- @return A transformation object
+--
+function Transform.new_scaling(x_scale, y_scale)
+ return { x_scale, 0, 0, y_scale or x_scale, 0, 0 }
+end
+
+
+
+
+---
+-- Concatenate two transformation matrices, returning the new one.
+--
+-- @param a The first transformation
+-- @param b The second transformation
+--
+-- @return The transformation representing first applying |b| and then
+-- applying |a|.
+--
+function Transform.concat(a,b)
+ local a1, a2, a3, a4, a5, a6, b1, b2, b3, b4, b5, b6 =
+ a[1], a[2], a[3], a[4], a[5], a[6], b[1], b[2], b[3], b[4], b[5], b[6]
+ return { a1*b1 + a2*b3, a1*b2 + a2*b4,
+ a3*b1 + a4*b3, a3*b2 + a4*b4,
+ a1*b5 + a2*b6 + a5, a3*b5 + a4*b6 + a6 }
+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
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/model.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/model.lua
new file mode 100644
index 0000000000..4a0130488b
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/model.lua
@@ -0,0 +1,26 @@
+-- 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$
+
+
+
+-- Imports
+
+require "pgf"
+require "pgf.gd"
+
+
+-- Declare namespace
+pgf.gd.model = {}
+
+
+-- Done
+
+return pgf.gd.model \ No newline at end of file
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/model/Arc.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/model/Arc.lua
new file mode 100644
index 0000000000..e007e9b59b
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/model/Arc.lua
@@ -0,0 +1,653 @@
+-- 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$
+
+
+---
+-- An arc is a light-weight object representing an arc from a vertex
+-- in a graph to another vertex. You may not create an |Arc| by
+-- yourself, which is why there is no |new| method, arc creation is
+-- done by the Digraph class.
+--
+-- Every arc belongs to exactly one graph. If you want the same arc in
+-- another graph, you need to newly connect two vertices in the other graph.
+--
+-- You may read the |head| and |tail| fields, but you may not write
+-- them. In order to store data for an arc, use |Storage| objects.
+--
+-- Between any two vertices of a graph there can be only one arc, so
+-- all digraphs are always simple graphs. However, in the
+-- specification of a graph (the syntactic digraph), there might
+-- be multiple edges between two vertices. This means, in particular,
+-- that an arc has no |options| field. Rather, it has several
+-- |optionsXxxx| functions, that will search for options in all of the
+-- syntactic edges that ``belong'' to an edge.
+--
+-- In order to \emph{set} options of the edges, you can set the
+-- |generated_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.
+--
+-- Finally, there is also an |animations| field, which, similarly to
+-- the |generated_options|, gets written back during a sync when it is
+-- not |nil|.
+--
+-- In detail, the following happens: Even though an arc has a |path|,
+-- |generated_options|, and |animations| fields, 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
+-- it is (namely, |nil|). If you want to have all edges along a path
+-- to follow a certain path, set the |path| field of the arc to the
+-- path you desire (typically, using the |setPolylinePath| or a
+-- similar method). This will cause all syntactic edges underlying the
+-- arc to be set to the specified path. In the event that you want to
+-- set different paths for the edges underlying a single arc
+-- differently, set the |path| fields of these edges and set the
+-- |path| field of the arc to |nil|. This will disable the syncing for
+-- the arc and will cause the edge |paths| to remain untouched.
+--
+-- @field tail The tail vertex of the arc.
+-- @field head The head vertex of the arc. May be the same as the tail
+-- in case of a loop.
+-- @field path If non-nil, the path of the arc. See the description
+-- above.
+-- @field generated_options If non-nil, some options to be passed back
+-- to the original syntactic edges, see the description above.
+-- @field animations If non-nil, some animations to be passed back
+-- to the original syntactic edges. See the description of the
+-- |animations| field for |Vertex| for details on the syntax.
+-- @field syntactic_edges In case this arc is an arc in the syntactic
+-- 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
+
+
+-- Namespace
+
+require("pgf.gd.model").Arc = Arc
+
+
+-- Imports
+
+local Path = require 'pgf.gd.model.Path'
+local lib = require 'pgf.gd.lib'
+
+
+---
+-- Get an array of options of the syntactic edges corresponding to an arc.
+--
+-- An arc in a digraph is typically (but not always) present because
+-- there are one or more edges in the syntactic digraph between the
+-- tail and the head of the arc or between the head and the tail.
+--
+-- Since for every arc there can be several edges present in the
+-- syntactic digraph, an option like |length| may have
+-- been given multiple times for the edges corresponding to the arc.
+--
+-- If your algorithm gets confused by multiple edges, try saying
+-- |a:options(your_option)|. This will always give the ``most
+-- sensible'' choice of the option if there are multiple edges
+-- corresponding to the same arc.
+--
+-- @param option A string option like |"length"|.
+--
+-- @return A table with the following contents:
+-- %
+-- \begin{enumerate}
+-- \item It is an array of all values the option has for edges
+-- corresponding to |self| in the syntactic digraph. Suppose, for
+-- instance, you write the following:
+-- %
+--\begin{codeexample}[code only]
+--graph {
+-- tail -- [length=1] head, % multi edge 1
+-- tail -- [length=3] head, % mulit edge 2
+-- head -- [length=8] tail, % multi edge 3
+-- tail -- head, % multi edge 4
+-- head -- [length=7] tail, % multi edge 5
+-- tail -- [length=2] head, % multi edge 6
+--}
+--\end{codeexample}
+-- %
+-- Suppose, furthermore, that |length| has been setup as an edge
+-- option. Now suppose that |a| is the arc from the vertex |tail| to
+-- the vertex |head|. Calling |a:optionsArray('length')| will
+-- yield the array part |{1,3,2,8,7}|. The reason for the ordering is
+-- as follows: First come all values |length| had for syntactic edges
+-- going from |self.tail| to |self.head| in the order they appear in the
+-- graph description. Then come all values the options has for syntactic
+-- edges going from |self.head| to |self.tail|. The reason for this
+-- slightly strange behavior is that many algorithms do not really
+-- care whether someone writes |a --[length=1] b| or
+-- |b --[length=1] a|; in both cases they would ``just'' like to know
+-- that the length is~|1|.
+--
+-- \item There is field called |aligned|, which is an array storing
+-- the actual syntactic edge objects whose values can be found in the
+-- array part of the returned table. However, |aligned| contains only
+-- the syntactic edges pointing ``in the same direction'' as the arc,
+-- that is, the tail and head of the syntactic edge are the same as
+-- those of the arc. In the above example, this array would contain
+-- the edges with the comment numbers |1|, |2|, and |6|.
+--
+-- Using the length of this array and the fact that the ``aligned''
+-- values come first in the table, you can easily iterate over the
+-- |option|'s values of only those edges that are aligned with the arc:
+-- %
+--\begin{codeexample}[code only, tikz syntax=false]
+--local a = g:arc(tail.head) -- some arc
+--local opt = a:optionsArray('length')
+--local sum = 0
+--for i=1,#opt.aligned do
+-- sum = sum + opt[i]
+--end
+--\end{codeexample}
+-- %
+-- \item There is a field called |anti_aligned|, which is an array
+-- containing exactly the edges in the array part of the table not
+-- aligned with the arc. The numbering start at |1| as usual, so the
+-- $i$th entry of this table corresponds to the entry at position $i +
+-- \verb!#opt.aligned!$ of the table.
+-- \end{enumerate}
+--
+function Arc:optionsArray(option)
+
+ local cache = self.option_cache
+ local t = cache[option]
+ if t then
+ return t
+ end
+
+ -- Accumulate the edges for which the option is set:
+ local tail = self.tail
+ local head = self.head
+ local s_graph = self.syntactic_digraph
+
+ local arc = s_graph:arc(tail, head)
+ local aligned = {}
+ if arc then
+ for _,m in ipairs(arc.syntactic_edges) do
+ if m.options[option] ~= nil then
+ aligned[#aligned + 1] = m
+ end
+ end
+ table.sort(aligned, function (a,b) return a.event.index < b.event.index end)
+ end
+
+ local arc = head ~= tail and s_graph:arc(head, tail)
+ local anti_aligned = {}
+ if arc then
+ for _,m in ipairs(arc.syntactic_edges) do
+ if m.options[option] ~= nil then
+ anti_aligned[#anti_aligned + 1] = m
+ end
+ end
+ table.sort(anti_aligned, function (a,b) return a.event.index < b.event.index end)
+ end
+
+ -- Now merge them together
+ local t = { aligned = aligned, anti_aligned = anti_aligned }
+ for i=1,#aligned do
+ t[i] = aligned[i].options[option]
+ end
+ for i=1,#anti_aligned do
+ t[#t+1] = anti_aligned[i].options[option]
+ end
+ cache[option] = t
+
+ return t
+end
+
+
+
+---
+-- Returns the first option, that is, the first entry of
+-- |Arc:optionsArray(option)|. However, if the |only_aligned|
+-- parameter is set to true and there is no option with any aligned
+-- syntactic edge, |nil| is returned.
+--
+-- @param option An option
+-- @param only_aligned If true, only aligned syntactic edges will be
+-- considered.
+-- @return The first entry of the |optionsArray|
+function Arc:options(option, only_aligned)
+ if only_aligned then
+ local opt = self:optionsArray(option)
+ if #opt.aligned > 0 then
+ return opt[1]
+ end
+ else
+ return self:optionsArray(option)[1]
+ end
+end
+
+
+
+
+---
+-- Get an accumulated value of an option of the syntactic edges
+-- corresponding to an arc.
+--
+-- @param option The option of interest
+-- @param accumulator A function taking two values. When there are
+-- more than one syntactic edges corresponding to |self| for which the
+-- |option| is set, this function will be called repeatedly for the
+-- different values. The first time it will be called for the first
+-- two values. Next, it will be called for the result of this call and
+-- the third value, and so on.
+-- @param only_aligned A boolean. If true, only the aligned syntactic
+-- edges will be considered.
+--
+-- @return If the option is not set for any (aligned) syntactic edges
+-- corresponding to |self|, |nil| is returned. If there is exactly one
+-- edge, the value of this edge is returned. Otherwise, the result of
+-- repeatedly applying the |accumulator| function as described
+-- above.
+--
+-- The result is cached, repeated calls will not invoke the
+-- |accumulator| function again.
+--
+-- @usage Here is typical usage:
+-- %
+--\begin{codeexample}[code only, tikz syntax=false]
+--local total_length = a:optionsAccumulated('length', function (a,b) return a+b end) or 0
+--\end{codeexample}
+--
+function Arc:optionsAccumulated(option, accumulator, only_aligned)
+ local opt = self:options(option)
+ if only_aligned then
+ local aligned = opt.aligned
+ local v = aligned[accumulator]
+ if v == nil then
+ v = opt[1]
+ for i=2,#aligned do
+ v = accumulator(v, opt[i])
+ end
+ align[accumulator] = v
+ end
+ return v
+ else
+ local v = opt[accumulator]
+ if v == nil then
+ v = opt[1]
+ for i=2,#opt do
+ v = accumulator(v, opt[i])
+ end
+ opt[accumulator] = v
+ end
+ return v
+ end
+end
+
+
+
+---
+-- Compute the syntactic head and tail of an arc. For this, we have a
+-- look at the syntactic digraph underlying the arc. If there is at
+-- least once syntactic edge going from the arc's tail to the arc's
+-- head, the arc's tail and head are returned. Otherwise, we test
+-- whether there is a syntactic edge in the other direction and, if
+-- so, return head and tail in reverse order. Finally, if there is no
+-- syntactic edge at all corresponding to the arc in either direction,
+-- |nil| is returned.
+--
+-- @return The syntactic tail
+-- @return The syntactic head
+
+function Arc:syntacticTailAndHead ()
+ local s_graph = self.syntactic_digraph
+ local tail = self.tail
+ local head = self.head
+ if s_graph:arc(tail, head) then
+ return tail, head
+ elseif s_graph:arc(head, tail) then
+ return head, tail
+ end
+end
+
+
+---
+-- Compute the point cloud.
+--
+-- @return This method will return the ``point cloud'' of an arc,
+-- which is an array of all points that must be rotated and shifted
+-- along with the endpoints of an edge.
+--
+function Arc:pointCloud ()
+ if self.cached_point_cloud then
+ return self.cached_point_cloud -- cached
+ end
+ local cloud = {}
+ local a = self.syntactic_digraph:arc(self.tail,self.head)
+ if a then
+ for _,e in ipairs(a.syntactic_edges) do
+ for _,p in ipairs(e.path) do
+ if type(p) == "table" then
+ cloud[#cloud + 1] = p
+ end
+ end
+ end
+ end
+ self.cached_point_cloud = cloud
+ return cloud
+end
+
+
+
+---
+-- Compute an event index for the arc.
+--
+-- @return The lowest event index of any edge involved
+-- in the arc (or nil, if there is no syntactic edge).
+--
+function Arc:eventIndex ()
+ if self.cached_event_index then
+ return self.cached_event_index
+ end
+ local head = self.head
+ local tail = self.tail
+ local e = math.huge
+ local a = self.syntactic_digraph:arc(tail,head)
+ if a then
+ for _,m in ipairs(a.syntactic_edges) do
+ e = math.min(e, m.event.index)
+ end
+ end
+ local a = head ~= tail and self.syntactic_digraph:arc(head,tail)
+ if a then
+ for _,m in ipairs(a.syntactic_edges) do
+ e = math.min(e, m.event.index)
+ end
+ end
+ self.cached_event_index = e
+ return e
+end
+
+
+
+
+---
+-- The span collector
+--
+-- This method returns the top (that is, smallest) priority of any
+-- edge involved in the arc.
+--
+-- The priority of an edge is computed as follows:
+-- %
+-- \begin{enumerate}
+-- \item If the option |"span priority"| is set, this number
+-- will be used.
+-- \item If the edge has the same head as the arc, we lookup the key\\
+-- |"span priority " .. edge.direction|. If set, we use this value.
+-- \item If the edge has a different head from the arc (the arc is
+-- ``reversed'' with respect to the syntactic edge), we lookup the key
+-- |"span priority reversed " .. edge.direction|. If set, we use this value.
+-- \item Otherwise, we use priority 5.
+-- \end{enumerate}
+--
+-- @return The priority of the arc, as described above.
+--
+function Arc:spanPriority()
+ if self.cached_span_priority then
+ return self.cached_span_priority
+ end
+
+ local head = self.head
+ local tail = self.tail
+ local min
+ local g = self.syntactic_digraph
+
+ local a = g:arc(tail,head)
+ if a then
+ for _,m in ipairs(a.syntactic_edges) do
+ local p =
+ m.options["span priority"] or
+ lib.lookup_option("span priority " .. m.direction, m, g)
+
+ min = math.min(p or 5, min or math.huge)
+ end
+ end
+
+ local a = head ~= tail and g:arc(head,tail)
+ if a then
+ for _,m in ipairs(a.syntactic_edges) do
+ local p =
+ m.options["span priority"] or
+ lib.lookup_option("span priority reversed " .. m.direction, m, g)
+
+ min = math.min(p or 5, min or math.huge)
+ end
+ end
+
+ self.cached_span_priority = min or 5
+
+ return min or 5
+end
+
+
+
+
+
+
+---
+-- 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 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]
+--...
+--local arc = g:connect(s,t)
+--arc:setPolylinePath { Coordinate.new(x,y), Coordinate.new(x1,y1) }
+--...
+--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.
+--
+function Arc:sync()
+ if self.path then
+ local path = self.path
+ 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
+ local clone = path:clone()
+ for i=1,#clone do
+ local p = clone[i]
+ if type(p) == "function" then
+ clone[i] = p(e)
+ if type(clone[i]) == "table" then
+ clone[i] = clone[i]:clone()
+ end
+ end
+ end
+ e.path = clone
+ 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
+ local clone = path:reversed()
+ for i=1,#clone do
+ local p = clone[i]
+ if type(p) == "function" then
+ clone[i] = p(e)
+ if type(clone[i]) == "table" then
+ clone[i] = clone[i]:clone()
+ end
+ end
+ end
+ e.path = clone
+ 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
+ if self.animations 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.animations) do
+ e.animations[#e.animations+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.animations) do
+ e.animations[#e.animations+1] = o
+ end
+ end
+ end
+ end
+end
+
+
+---
+-- This method returns a ``coordinate factory'' that can be used as
+-- the coordinate of a |moveto| at the beginning of a path starting at
+-- the |tail| of the arc. Suppose you want to create a path starting
+-- at the tail vertex, going to the coordinate $(10,10)$ and ending at
+-- the head vertex. The trouble is that when you create the path
+-- corresponding to this route, you typically do not know where the
+-- tail vertex is going to be. Even if that \emph{has} already been
+-- settled, you will still have the problem that different edges
+-- underlying the arc may wish to start their paths at different
+-- anchors inside the tail vertex. In such cases, you use this
+-- method to get a function that will, later on, compute the correct
+-- position of the anchor as needed.
+--
+-- Here is the code you would use to create the above-mentioned path:
+-- %
+--\begin{codeexample}[code only, tikz syntax=false]
+--local a = g:connect(tail,head)
+--...
+--arc.path = Path.new()
+--arc.path:appendMoveto(arc:tailAnchorForArcPath())
+--arc.path:appendLineto(10, 10)
+--arc.path:appendLineto(arc:headAnchorForArcPath())
+--\end{codeexample}
+--
+-- Normally, however, you will not write code as detailed as the above
+-- and you would just write instead of the last three lines:
+-- %
+--\begin{codeexample}[code only, tikz syntax=false]
+--arc:setPolylinePath { Coordinate.new (10, 10) }
+--\end{codeexample}
+
+function Arc:tailAnchorForArcPath()
+ return function (edge)
+ local a = edge.options['tail anchor']
+ if a == "" then
+ a = "center"
+ end
+ return self.tail:anchor(a) + self.tail.pos
+ end
+end
+
+---
+-- See |Arc:tailAnchorForArcPath|.
+
+function Arc:headAnchorForArcPath()
+ return function (edge)
+ local a = edge.options['head anchor']
+ if a == "" then
+ a = "center"
+ end
+ return self.head:anchor(a) + self.head.pos
+ end
+end
+
+
+
+---
+-- Setup the |path| field of an arc in such a way that it corresponds
+-- to a sequence of straight line segments starting at the tail's
+-- anchor and ending at the head's anchor.
+--
+-- @param coordinates An array of |Coordinates| through which the line
+-- will go through.
+
+function Arc:setPolylinePath(coordinates)
+ local p = Path.new ()
+
+ p:appendMoveto(self:tailAnchorForArcPath())
+
+ for _,c in ipairs(coordinates) do
+ p:appendLineto(c)
+ end
+
+ p:appendLineto(self:headAnchorForArcPath())
+
+ self.path = p
+end
+
+
+
+
+-- Returns a string representation of an arc. This is mainly for debugging
+--
+-- @return The Arc as string.
+--
+function Arc:__tostring()
+ return tostring(self.tail) .. "->" .. tostring(self.head)
+end
+
+
+-- Done
+
+return Arc
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/model/Collection.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/model/Collection.lua
new file mode 100644
index 0000000000..abc454b7db
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/model/Collection.lua
@@ -0,0 +1,208 @@
+-- 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$
+
+
+---
+-- A collection is essentially a subgraph of a graph, that is, a
+-- ``collection'' of some nodes and some edges of the graph. The name
+-- ``collection'' was chosen over ``subgraph'' since the latter are
+-- often thought of as parts of a graph that are rendered in a special
+-- way (such as being surrounded by a rectangle), while collections
+-- are used to model such diverse things as hyperedges, sets of
+-- vertices that should be on the same level in a layered algorithm,
+-- or, indeed, subgraphs that are rendered in a special way.
+--
+-- Collections are grouped into ``kinds''. All collections of a given
+-- kind can be accessed by algorithms through an array whose elements
+-- are the collections. On the display layer, for each kind a separate
+-- key is available to indicate that a node or an edge belongs to a
+-- collection.
+--
+-- Collections serve two purposes: First, they can be seen as ``hints''
+-- to graph drawing algorithms that certain nodes and/or edges ``belong
+-- together''. For instance, collections of kind |same layer| are used
+-- by the Sugiyama algorithm to group together nodes that should appear
+-- at the same height of the output. Second, since collections are also
+-- passed back to the display layer in a postprocessing step, they can be
+-- used to render complicated concepts such as hyperedges (which are
+-- just collections of nodes, after all) or subgraphs.
+--
+-- @field kind The ``kind'' of the collection.
+--
+-- @field vertices A lookup table of vertices (that is, both an array
+-- with the vertices in the order in which they appear as well as a
+-- table such that |vertices[vertex] == true| whenever |vertex| is
+-- present in the table.
+--
+-- @field edges A lookup table of edges (not arcs!).
+--
+-- @field options An options table. This is the table of options that
+-- was in force when the collection was created.
+--
+-- @field child_collections An array of all collections that are
+-- direct children of this collection (that is,
+-- they were defined while the current collection was the most
+-- recently defined collection on the options stack). However, you
+-- should use the methods |children|, |descendants|, and so to access
+-- this field.
+--
+-- @field parent_collection The parent collection of the current
+-- collection. This field may be |nil| in case a collection has no parent.
+--
+-- @field event An |Event| object that was create for this
+-- collection. Its |kind| will be |"collection"| while its |parameter|
+-- will be the collection kind.
+
+local Collection = {}
+Collection.__index = Collection
+
+
+-- Namespace
+
+require("pgf.gd.model").Collection = Collection
+
+
+-- Imports
+local Storage = require "pgf.gd.lib.Storage"
+
+
+
+---
+-- Creates a new collection. You should not call this function
+-- directly, it is called by the interface classes.
+--
+-- @param t A table of initial values. The field |t.kind| must be a
+-- nonempty string.
+--
+-- @return The new collection
+--
+function Collection.new(t)
+ assert (type(t.kind) == "string" and t.kind ~= "", "collection kind not set")
+
+ return setmetatable(
+ {
+ vertices = t.vertices or {},
+ edges = t.edges or {},
+ options = t.options or {},
+ generated_options = t.generated_options or {},
+ kind = t.kind,
+ event = t.event,
+ child_collections = t.child_collections or {},
+ }, Collection)
+end
+
+
+
+
+--
+-- An internal function for registering a collection as child of
+-- another collection. The collection |self| will be made a child
+-- collection of |parent|.
+--
+-- @param parent A collection.
+
+function Collection:registerAsChildOf(parent)
+ self.parent = parent
+ if parent then
+ assert (getmetatable(parent) == Collection, "parent must be a collection")
+ parent.child_collections[#parent.child_collections+1] = self
+ end
+end
+
+
+
+---
+-- A collection can have any number of \emph{child collections}, which
+-- are collections nested inside the collection. You can access the
+-- array of these children through this method. You may not modify
+-- the array returned by this function.
+--
+-- @return The array of children of |self|.
+--
+function Collection:children()
+ return self.child_collections
+end
+
+
+---
+-- This method works like the |children| method. However, the tree of
+-- collections is, conceptually, contracted by considering only these
+-- collections that have the |kind| given as parameter. For instance,
+-- if |self| has a child collection of a kind different from |kind|,
+-- but this child collection has, in turn, a child collection of kind
+-- |kind|, this latter child collection will be included in the array
+-- -- but not any of its child collections.
+--
+-- @param kind The collection kind to which the tree of collections
+-- should be restricted.
+--
+-- @return The array of children of |self| in this contracted tree.
+--
+function Collection:childrenOfKind(kind)
+ local function rec (c, a)
+ for _,d in ipairs(c.child_collections) do
+ if d.kind == kind then
+ a[#a + 1] = d
+ else
+ rec (d, a)
+ end
+ end
+ return a
+ end
+ return rec(self, {})
+end
+
+
+---
+-- The descendants of a collection are its children, plus their
+-- children, plus their children, and so on.
+--
+-- @return An array of all descendants of |self|. It will be in
+-- preorder.
+
+function Collection:descendants()
+ local function rec (c, a)
+ for _,d in ipairs(c.child_collections) do
+ a[#a + 1] = d
+ rec (d, a)
+ end
+ return a
+ end
+ return rec(self, {})
+end
+
+
+
+---
+-- The descendants of a collection of the given |kind|.
+--
+-- @param kind A collection kind.
+--
+-- @return An array of all descendants of |self| of the given |kind|.
+
+function Collection:descendantsOfKind(kind)
+ local function rec (c, a)
+ for _,d in ipairs(c.child_collections) do
+ if d.kind == kind then
+ a[#a + 1] = d
+ end
+ rec (d, a)
+ end
+ return a
+ end
+ return rec(self, {})
+end
+
+
+
+-- Done
+
+return Collection
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/model/Coordinate.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/model/Coordinate.lua
new file mode 100644
index 0000000000..3a4448ab08
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/model/Coordinate.lua
@@ -0,0 +1,306 @@
+-- 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$
+
+
+---
+-- A Coordinate models a position on the drawing canvas.
+--
+-- It has an |x| field and a |y| field, which are numbers that will be
+-- interpreted as \TeX\ points (1/72.27th of an inch). The $x$-axis goes
+-- right and the $y$-axis goes up.
+--
+-- @field x
+-- @field y
+--
+-- There is also a static field called |origin| that is always equal to the origin.
+
+local Coordinate = {}
+Coordinate.__index = Coordinate
+
+
+-- Namespace
+
+require("pgf.gd.model").Coordinate = Coordinate
+
+
+
+
+--- Creates a new coordinate.
+--
+-- @param x The $x$ value
+-- @param y The $y$ value
+--
+-- @return A coordinate
+--
+function Coordinate.new(x,y)
+ return setmetatable( {x=x, y=y}, Coordinate)
+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|
+--
+function Coordinate:clone()
+ return setmetatable( { x = self.x, y = self.y }, Coordinate)
+end
+
+
+
+--- Apply a transformation matrix to a coordinate,
+-- see |pgf.gd.lib.Transform| for details.
+--
+-- @param t A transformation.
+
+function Coordinate:apply(t)
+ local x = self.x
+ local y = self.y
+ self.x = t[1]*x + t[2]*y + t[5]
+ self.y = t[3]*x + t[4]*y + t[6]
+end
+
+
+--- Shift a coordinate
+--
+-- @param a An $x$ offset
+-- @param b A $y$ offset
+
+function Coordinate:shift(a,b)
+ self.x = self.x + a
+ self.y = self.y + b
+end
+
+
+---
+-- ``Unshift'' a coordinate (which is the same as shifting by the
+-- inversed coordinate; only faster).
+--
+-- @param a An $x$ offset
+-- @param b A $y$ offset
+
+function Coordinate:unshift(a,b)
+ self.x = self.x - a
+ self.y = self.y - b
+end
+
+
+---
+-- Like |shift|, only for coordinate parameters.
+--
+-- @param c Another coordinate. The $x$- and $y$-values of |self| are
+-- increased by the $x$- and $y$-values of this coordinate.
+
+function Coordinate:shiftByCoordinate(c)
+ self.x = self.x + c.x
+ self.y = self.y + c.y
+end
+
+
+---
+-- Like |unshift|, only for coordinate parameters.
+--
+-- @param c Another coordinate.
+
+function Coordinate:unshiftByCoordinate(c)
+ self.x = self.x - c.x
+ self.y = self.y - c.y
+end
+
+
+---
+-- Moves the coordinate a fraction of |f| along a straight line to |c|.
+--
+-- @param c Another coordinate
+-- @param f A fraction
+
+function Coordinate:moveTowards(c,f)
+ self.x = self.x + f*(c.x-self.x)
+ self.y = self.y + f*(c.y-self.y)
+end
+
+
+
+--- Scale a coordinate by a factor
+--
+-- @param s A factor.
+
+function Coordinate:scale(s)
+ self.x = s*self.x
+ self.y = s*self.y
+end
+
+
+
+
+---
+-- Add two coordinates, yielding a new coordinate. Note that it will
+-- be a lot faster to call shift, whenever this is possible.
+--
+-- @param a A coordinate
+-- @param b A coordinate
+
+function Coordinate.__add(a,b)
+ return setmetatable({ x = a.x + b.x, y = a.y + b.y }, Coordinate)
+end
+
+
+---
+-- Subtract two coordinates, yielding a new coordinate. Note that it will
+-- be a lot faster to call unshift, whenever this is possible.
+--
+-- @param a A coordinate
+-- @param b A coordinate
+
+function Coordinate.__sub(a,b)
+ return setmetatable({ x = a.x - b.x, y = a.y - b.y }, Coordinate)
+end
+
+
+---
+-- The unary minus (mirror the coordinate against the origin).
+--
+-- @param a A coordinate
+
+function Coordinate.__unm(a)
+ return setmetatable({ x = - a.x, y = - a.y }, Coordinate)
+end
+
+
+---
+-- The multiplication operator. Its effect depends on the parameters:
+-- If both are coordinates, their dot-product is returned. If exactly
+-- one of them is a coordinate and the other is a number, the scalar
+-- multiple of this coordinate is returned.
+--
+-- @param a A coordinate or a scalar
+-- @param b A coordinate or a scalar
+-- @return The dot product or scalar product.
+
+function Coordinate.__mul(a,b)
+ if getmetatable(a) == Coordinate then
+ if getmetatable(b) == Coordinate then
+ return a.x * b.x + a.y * b.y
+ else
+ return setmetatable({ x = a.x * b, y = a.y *b }, Coordinate)
+ end
+ else
+ return setmetatable({ x = a * b.x, y = a * b.y }, Coordinate)
+ end
+end
+
+---
+-- The division operator. Returns the scalar division of a coordinate
+-- by a scalar.
+--
+-- @param a A coordinate
+-- @param b A scalar (not equal to zero).
+-- @return The scalar product or a * (1/b).
+
+function Coordinate.__div(a,b)
+ return setmetatable({ x = a.x / b, y = a.y / b }, Coordinate)
+end
+
+
+---
+-- The norm function. Returns the norm of a coordinate.
+--
+-- @param a A coordinate
+-- @return The norm of the coordinate
+
+function Coordinate:norm()
+ return math.sqrt(self.x * self.x + self.y * self.y)
+end
+
+
+---
+-- Normalize a vector: Ensure that it has length 1. If the vector used
+-- to be the 0-vector, it gets replaced by (1,0).
+--
+
+function Coordinate:normalize()
+ local x, y = self.x, self.y
+ if x == 0 and y == 0 then
+ self.x = 1
+ else
+ local norm = math.sqrt(x*x+y*y)
+ self.x = x / norm
+ self.y = y / norm
+ end
+end
+
+
+---
+-- Normalized version of a vector: Like |normalize|, only the result is
+-- returned in a new vector.
+--
+-- @return Normalized version of |self|
+
+function Coordinate:normalized()
+ local x, y = self.x, self.y
+ if x == 0 and y == 0 then
+ return setmetatable({ x = 1, y = 0 }, Coordinate)
+ else
+ local norm = math.sqrt(x*x+y*y)
+ return setmetatable({ x = x/norm, y = y/norm }, Coordinate)
+ end
+end
+
+
+
+---
+-- Compute a bounding box around an array of coordinates
+--
+-- @param array An array of coordinates
+--
+-- @return |min_x| The minimum $x$ value of the bounding box of the array
+-- @return |min_y| The minimum $y$ value
+-- @return |max_x|
+-- @return |max_y|
+-- @return |center_x| The center of the bounding box
+-- @return |center_y|
+
+function Coordinate.boundingBox(array)
+ if #array > 0 then
+ local min_x, min_y = math.huge, math.huge
+ local max_x, max_y = -math.huge, -math.huge
+
+ for i=1,#array do
+ local c = array[i]
+ local x = c.x
+ local y = c.y
+ if x < min_x then min_x = x end
+ if y < min_y then min_y = y end
+ if x > max_x then max_x = x end
+ if y > max_y then max_y = y end
+ end
+
+ return min_x, min_y, max_x, max_y, (min_x+max_x) / 2, (min_y+max_y) / 2
+ end
+end
+
+
+
+
+-- Returns a string representation of an arc. This is mainly for debugging
+--
+-- @return The Arc as string.
+--
+function Coordinate:__tostring()
+ return "(" .. self.x .. "pt," .. self.y .. "pt)"
+end
+
+
+-- Done
+
+return Coordinate \ No newline at end of file
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/model/Digraph.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/model/Digraph.lua
new file mode 100644
index 0000000000..30b600e308
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/model/Digraph.lua
@@ -0,0 +1,849 @@
+-- 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$
+
+
+
+
+---
+-- Each |Digraph| instance models a \emph{directed, simple}
+-- graph. ``Directed'' means that all edges ``point'' from a head node
+-- to a tail node. ``Simple'' means that between any nodes there can be
+-- (at most) one edge. Since these properties are a bit at odds with
+-- the normal behavior of ``nodes'' and ``edges'' in \tikzname,
+-- different names are used for them inside the |model| namespace:
+-- The class modeling ``edges'' is actually called |Arc| to stress
+-- that an arc has a specific ``start'' (the tail) and a specific
+-- ``end'' (the head). The class modeling ``nodes'' is actually called
+-- |Vertex|, just to stress that this is not a direct model of a
+-- \tikzname\ |node|, but can represent a arbitrary vertex of a graph,
+-- independently of whether it is an actual |node| in \tikzname.
+--
+-- \medskip
+-- \noindent\emph{Time Bounds.}
+-- Since digraphs are constantly created and modified inside the graph
+-- drawing engine, some care was taken to ensure that all operations
+-- work as quickly as possible. In particular:
+-- %
+-- \begin{itemize}
+-- \item Adding an array of $k$ vertices using the |add| method needs
+-- time $O(k)$.
+-- \item Adding an arc between two vertices needs time $O(1)$.
+-- \item Accessing both the |vertices| and the |arcs| fields takes time
+-- $O(1)$, provided only the above operations are used.
+-- \end{itemize}
+-- %
+-- Deleting vertices and arcs takes more time:
+-- %
+-- \begin{itemize}
+-- \item Deleting the vertices given in an array of $k$ vertices from a
+-- graph with $n$ vertices takes time $O(\max\{n,c\})$ where $c$ is the
+-- number of arcs between the to-be-deleted nodes and the remaining
+-- nodes. Note that this time bound in independent of~$k$. In
+-- particular, it will be much faster to delete many vertices by once
+-- calling the |remove| function instead of calling it repeatedly.
+-- \item Deleting an arc takes time $O(t_o+h_i)$ where $t_o$ is the
+-- number of outgoing arcs at the arc's tail and $h_i$ is the number
+-- of incoming arcs at the arc's head. After a call to |disconnect|,
+-- the next use of the |arcs| field will take time $O(|V| + |E|)$,
+-- while subsequent accesses take time $O(1)$ -- till the
+-- next use of |disconnect|. This means that once you start deleting
+-- arcs using |disconnect|, you should perform as many additional
+-- |disconnect|s as possible before accessing |arcs| one more.
+-- \end{itemize}
+--
+-- \medskip
+-- \noindent\emph{Stability.} The |vertices| field and the array
+-- returned by |Digraph:incoming| and |Digraph:outgoing| are
+-- \emph{stable} in the following sense: The ordering of the elements
+-- when you use |ipairs| on the will be the ordering in which the
+-- vertices or arcs were added to the graph. Even when you remove a
+-- vertex or an arc, the ordering of the remaining elements stays the
+-- same.
+--
+-- @field vertices This array contains the vertices that are part of
+-- the digraph. Internally, this array
+-- is an object of type |LookupTable|, but you can mostly treat it as
+-- if it were an array. In particular, you can iterate over its
+-- elements using |ipairs|, but you may not modify the array; use the
+-- |add| and |remove| methods, instead.
+--
+-- \begin{codeexample}[code only, tikz syntax=false]
+-- local g = Digraph.new {}
+--
+-- g:add { v1, v2 } -- Add vertices v1 and v2
+-- g:remove { v2 } -- Get rid of v2.
+--
+-- assert (g:contains(v1))
+-- assert (not g:contains(v2))
+-- \end{codeexample}
+--
+-- It is important to note that although each digraph stores a
+-- |vertices| array, the elements in this array are not exclusive to
+-- the digraph: A vertex can be an element of any number of
+-- digraphs. Whether or not a vertex is an element of digraph is not
+-- stored in the vertex, only in the |vertices| array of the
+-- digraph. To test whether a digraph contains a specific node, use the
+-- |contains| method, which takes time $O(1)$ to perform the test (this
+-- is because, as mentioned earlier, the |vertices| array is actually a
+-- |LookupTable| and for each vertex |v| the field |vertices[v]| will
+-- be true if, and only if, |v| is an element of the |vertices| array).
+--
+-- Do not use |pairs(g.vertices)| because this may cause your graph
+-- drawing algorithm to produce different outputs on different runs.
+--
+-- A slightly annoying effect of vertices being able to belong to
+-- several graphs at the same time is that the set of arcs incident to
+-- a vertex is not a property of the vertex, but rather of the
+-- graph. In other words, to get a list of all arcs whose tail is a
+-- given vertex |v|, you cannot say something like |v.outgoings| or
+-- perhaps |v:getOutgoings()|. Rather, you have to say |g:outgoing(v)|
+-- to get this list:
+-- %
+--\begin{codeexample}[code only, tikz syntax=false]
+--for _,a in ipairs(g:outgoing(v)) do -- g is a Digraph object.
+-- pgf.debug ("There is an arc leaving " .. tostring(v) ..
+-- " heading to " .. tostring(a.head))
+--end
+--\end{codeexample}
+-- %
+-- Naturally, there is also a method |g:incoming()|.
+--
+-- To iterate over all arcs of a graph you can say:
+-- %
+--\begin{codeexample}[code only, tikz syntax=false]
+--for _,v in ipairs(g.vertices) do
+-- for _,a in ipairs(g:outgoing(v)) do
+-- ...
+-- end
+--end
+--\end{codeexample}
+--
+-- However, it will often be more convenient and, in case the there
+-- are far less arcs than vertices, also faster to write
+--
+--\begin{codeexample}[code only, tikz syntax=false]
+--for _,a in ipairs(g.arcs) do
+-- ...
+--end
+--\end{codeexample}
+--
+-- @field arcs For any two vertices |t| and |h| of a graph, there may
+-- or may not be
+-- an arc from |t| to |h|. If this is the case, there is an |Arc|
+-- object that represents this arc. Note that, since |Digraph|s are
+-- always simple graphs, there can be at most one such object for every
+-- pair of vertices. However, you can store any information you like for
+-- an |Arc| through a |Storage|, see the |Storage| class for
+-- details. Each |Arc| for an edge of the syntactic digraph stores
+-- an array called |syntactic_edges| of all the multiple edges that
+-- are present in the user's input.
+--
+-- Unlike vertices, the arc objects of a graph are always local to a
+-- graph; an |Arc| object can never be part of two digraphs at the same
+-- time. For this reason, while for vertices it makes sense to create
+-- |Vertex| objects independently of any |Digraph| objects, it is not
+-- possible to instantiate an |Arc| directly: only the |Digraph| method
+-- |connect| is allowed to create new |Arc| objects and it will return
+-- any existing arcs instead of creating new ones, if there is already
+-- an arc present between two nodes.
+--
+-- The |arcs| field of a digraph contains a |LookupTable| of all arc
+-- objects present in the |Digraph|. Although you can access this field
+-- normally and use it in |ipairs| to iterate over all arcs of a graph,
+-- note that this array is actually ``reconstructed lazily'' whenever
+-- an arc is deleted from the graph. What happens is the following: As
+-- long as you just add arcs to a graph, the |arcs| array gets updated
+-- normally. However, when you remove an arc from a graph, the arc does
+-- not get removed from the |arcs| array (which would be an expensive
+-- operation). Instead, the |arcs| array is invalidated (internally set
+-- to |nil|), allowing us to perform a |disconnect| in time
+-- $O(1)$. The |arcs| array is then ignored until the next time it is
+-- accessed, for instance when a user says |ipairs(g.arcs)|. At this
+-- point, the |arcs| array is reconstructed by adding all arcs of all
+-- nodes to it.
+--
+-- The bottom line of the behavior of the |arcs| field is that (a) the
+-- ordering of the elements may change abruptly whenever you remove an
+-- arc from a graph and (b) performing $k$ |disconnect| operations in
+-- sequence takes time $O(k)$, provided you do not access the |arcs|
+-- field between calls.
+--
+-- @field syntactic_digraph is a reference to the syntactic digraph
+-- from which this graph stems ultimately. This may be a cyclic
+-- reference to the graph itself.
+-- @field options If present, it will be a table storing
+-- the options set for the syntactic digraph.
+--
+local Digraph = {}
+
+local function recalc_arcs (digraph)
+ local arcs = {}
+ local vertices = digraph.vertices
+ for i=1,#vertices do
+ local out = vertices[i].outgoings[digraph]
+ for j=1,#out do
+ arcs[#arcs + 1] = out[j]
+ end
+ end
+ digraph.arcs = arcs
+ return arcs
+end
+
+Digraph.__index =
+ function (t, k)
+ if k == "arcs" then
+ return recalc_arcs(t)
+ else
+ return rawget(Digraph,k)
+ end
+ end
+
+
+
+-- Namespace
+require("pgf.gd.model").Digraph = Digraph
+
+-- Imports
+local Arc = require "pgf.gd.model.Arc"
+local LookupTable = require "pgf.gd.lib.LookupTable"
+local Vertex = require "pgf.gd.model.Vertex"
+
+
+
+
+
+---
+-- Graphs are created using the |new| method, which takes a table of
+-- |initial| values as input (like most |new| methods in the graph
+-- drawing system). It is permissible that this table of initial values
+-- has a |vertices| field, in which case this array will be copied. In
+-- contrast, an |arcs| field in the table will be ignored -- newly
+-- created graphs always have an empty arcs set. This means that
+-- writing |Digraph.new(g)| where |g| is a graph creates a new graph
+-- whose vertex set is the same as |g|'s, but where there are no edges:
+-- %
+--\begin{codeexample}[code only, tikz syntax=false]
+--local g = Digraph.new {}
+--g:add { v1, v2, v3 }
+--g:connect (v1, v2)
+--
+--local h = Digraph.new (g)
+--assert (h:contains(v1))
+--assert (not h:arc(v1, v2))
+--\end{codeexample}
+--
+-- To completely copy a graph, including all arcs, you have to write:
+--\begin{codeexample}[code only, tikz syntax=false]
+--local h = Digraph.new (g)
+--for _,a in ipairs(g.arcs) do h:connect(a.tail, a.head) end
+--\end{codeexample}
+--
+-- This operation takes time $O(1)$.
+--
+-- @param initial A table of initial values. It is permissible that
+-- this array contains a |vertices| field. In this
+-- case, this field must be an array and its entries
+-- must be nodes, which will be inserted. If initial
+-- has an |arcs| field, this field will be ignored.
+-- The table must contain a field |syntactic_digraph|,
+-- which should normally be the syntactic digraph of
+-- the graph, but may also be the string |"self"|, in
+-- which case it will be set to the newly created
+-- (syntactic) digraph.
+-- @return A newly-allocated digraph.
+--
+function Digraph.new(initial)
+ local digraph = {}
+ setmetatable(digraph, Digraph)
+
+ if initial then
+ for k,v in pairs(initial or {}) do
+ digraph [k] = v
+ end
+ end
+
+ local vertices = digraph.vertices
+ digraph.vertices = {}
+ digraph.arcs = {}
+
+ if vertices then
+ digraph:add(vertices)
+ end
+ return digraph
+end
+
+
+--- Add vertices to a digraph.
+--
+-- This operation takes time $O(|\verb!array!|)$.
+--
+-- @param array An array of to-be-added vertices.
+--
+function Digraph:add(array)
+ local vertices = self.vertices
+ for i=1,#array do
+ local v = array[i]
+ if not vertices[v] then
+ vertices[v] = true
+ vertices[#vertices + 1] = v
+ v.incomings[self] = {}
+ v.outgoings[self] = {}
+ end
+ end
+end
+
+
+--- Remove vertices from a digraph.
+--
+-- This operation removes an array of vertices from a graph. The
+-- operation takes time linear in the number of vertices, regardless of
+-- how many vertices are to be removed. Thus, it will be (much) faster
+-- to delete many vertices by first compiling them in an array and to
+-- then delete them using one call to this method.
+--
+-- This operation takes time $O(\max\{|\verb!array!|, |\verb!self.vertices!|\})$.
+--
+-- @param array The to-be-removed vertices.
+--
+function Digraph:remove(array)
+ local vertices = self.vertices
+
+ -- Mark all to-be-deleted nodes
+ for i=1,#array do
+ local v = array[i]
+ assert(vertices[v], "to-be-deleted node is not in graph")
+ vertices[v] = false
+ end
+
+ -- Disconnect them
+ for i=1,#array do
+ self:disconnect(array[i])
+ end
+
+ LookupTable.remove(self.vertices, array)
+end
+
+
+
+--- Test, whether a graph contains a given vertex.
+--
+-- This operation takes time $O(1)$.
+--
+-- @param v The vertex to be tested.
+--
+function Digraph:contains(v)
+ return v and self.vertices[v] == true
+end
+
+
+
+
+---
+-- Returns the arc between two nodes, provided it exists. Otherwise,
+-- |nil| is returned.
+--
+-- This operation takes time $O(1)$.
+--
+-- @param tail The tail vertex
+-- @param head The head vertex
+--
+-- @return The arc object connecting them
+--
+function Digraph:arc(tail, head)
+ local out = tail.outgoings[self]
+ if out then
+ return out[head]
+ end
+end
+
+
+
+---
+-- Returns an array containing the outgoing arcs of a vertex. You may
+-- only iterate over his array using ipairs, not using pairs.
+--
+-- This operation takes time $O(1)$.
+--
+-- @param v The vertex
+--
+-- @return An array of all outgoing arcs of this vertex (all arcs
+-- whose tail is the vertex)
+--
+function Digraph:outgoing(v)
+ return assert(v.outgoings[self], "vertex not in graph")
+end
+
+
+
+---
+-- Sorts the array of outgoing arcs of a vertex. This allows you to
+-- later iterate over the outgoing arcs in a specific order.
+--
+-- This operation takes time $O(|\verb!outgoing!| \log |\verb!outgoings!|)$.
+--
+-- @param v The vertex
+-- @param f A comparison function that is passed to |table.sort|
+--
+function Digraph:sortOutgoing(v, f)
+ table.sort(assert(v.outgoings[self], "vertex not in graph"), f)
+end
+
+
+---
+-- Reorders the array of outgoing arcs of a vertex. The parameter array
+-- \emph{must} contain the same set of vertices as the outgoing array,
+-- but possibly in a different order.
+--
+-- This operation takes time $O(|\verb!outgoing!|)$, where |outgoing|
+-- is the array of |v|'s outgoing arcs in |self|.
+--
+-- @param v The vertex
+-- @param vertices An array containing the outgoing vertices in some order.
+--
+function Digraph:orderOutgoing(v, vertices)
+ local outgoing = assert (v.outgoings[self], "vertex not in graph")
+ assert (#outgoing == #vertices)
+
+ -- Create back hash
+ local lookup = {}
+ for i=1,#vertices do
+ lookup[vertices[i]] = i
+ end
+
+ -- Compute ordering of the arcs
+ local reordered = {}
+ for _,arc in ipairs(outgoing) do
+ reordered [lookup[arc.head]] = arc
+ end
+
+ -- Copy back
+ for i=1,#outgoing do
+ outgoing[i] = assert(reordered[i], "illegal vertex order")
+ end
+end
+
+
+
+--- See |outgoing|.
+--
+function Digraph:incoming(v)
+ return assert(v.incomings[self], "vertex not in graph")
+end
+
+
+---
+-- See |sortOutgoing|.
+--
+function Digraph:sortIncoming(v, f)
+ table.sort(assert(v.incomings[self], "vertex not in graph"), f)
+end
+
+
+---
+-- See |orderOutgoing|.
+--
+function Digraph:orderIncoming(v, vertices)
+ local incoming = assert (v.incomings[self], "vertex not in graph")
+ assert (#incoming == #vertices)
+
+ -- Create back hash
+ local lookup = {}
+ for i=1,#vertices do
+ lookup[vertices[i]] = i
+ end
+
+ -- Compute ordering of the arcs
+ local reordered = {}
+ for _,arc in ipairs(incoming) do
+ reordered [lookup[arc.head]] = arc
+ end
+
+ -- Copy back
+ for i=1,#incoming do
+ incoming[i] = assert(reordered[i], "illegal vertex order")
+ end
+end
+
+
+
+
+
+---
+-- Connects two nodes by an arc and returns the newly created arc
+-- object. If they are already connected, the existing arc is returned.
+--
+-- This operation takes time $O(1)$.
+--
+-- @param s The tail vertex
+-- @param t The head vertex (may be identical to |tail| in case of a
+-- loop)
+--
+-- @return The arc object connecting them (either newly created or
+-- already existing)
+--
+function Digraph:connect(s, t)
+ assert (s and t and self.vertices[s] and self.vertices[t], "trying connect nodes not in graph")
+
+ local s_outgoings = s.outgoings[self]
+ local arc = s_outgoings[t]
+
+ if not arc then
+ -- Ok, create and insert new arc object
+ arc = {
+ tail = s,
+ head = t,
+ option_cache = {},
+ syntactic_digraph = self.syntactic_digraph,
+ syntactic_edges = {}
+ }
+ setmetatable(arc, Arc)
+
+ -- Insert into outgoings:
+ s_outgoings [#s_outgoings + 1] = arc
+ s_outgoings [t] = arc
+
+ local t_incomings = t.incomings[self]
+ -- Insert into incomings:
+ t_incomings [#t_incomings + 1] = arc
+ t_incomings [s] = arc
+
+ -- Insert into arcs field, if it exists:
+ local arcs = rawget(self, "arcs")
+ if arcs then
+ arcs[#arcs + 1] = arc
+ end
+ end
+
+ return arc
+end
+
+
+
+
+---
+-- Disconnect either a single vertex |v| from all its neighbors (remove all
+-- incoming and outgoing arcs of this vertex) or, in case two nodes
+-- are given as parameter, remove the arc between them, if it exists.
+--
+-- This operation takes time $O(|I_v| + |I_t|)$, where $I_x$ is the set
+-- of vertices incident to $x$, to remove the single arc between $v$ and
+-- $v$. For a single vertex $v$, it takes time $O(\sum_{y: \text{there is some
+-- arc between $v$ and $y$ or $y$ and $v$}} |I_y|)$.
+--
+-- @param v The single vertex or the tail vertex
+-- @param t The head vertex
+--
+function Digraph:disconnect(v, t)
+ if t then
+ -- Case 2: Remove a single arc.
+ local s_outgoings = assert(v.outgoings[self], "tail node not in graph")
+ local t_incomings = assert(t.incomings[self], "head node not in graph")
+
+ if s_outgoings[t] then
+ -- Remove:
+ s_outgoings[t] = nil
+ for i=1,#s_outgoings do
+ if s_outgoings[i].head == t then
+ table.remove (s_outgoings, i)
+ break
+ end
+ end
+ t_incomings[v] = nil
+ for i=1,#t_incomings do
+ if t_incomings[i].tail == v then
+ table.remove (t_incomings, i)
+ break
+ end
+ end
+ self.arcs = nil -- invalidate arcs field
+ end
+ else
+ -- Case 1: Remove all arcs incident to v:
+
+ -- Step 1: Delete all incomings arcs:
+ local incomings = assert(v.incomings[self], "node not in graph")
+ local vertices = self.vertices
+
+ for i=1,#incomings do
+ local s = incomings[i].tail
+ if s ~= v and vertices[s] then -- skip self-loop and to-be-deleted nodes
+ -- Remove this arc from s:
+ local s_outgoings = s.outgoings[self]
+ s_outgoings[v] = nil
+ for i=1,#s_outgoings do
+ if s_outgoings[i].head == v then
+ table.remove (s_outgoings, i)
+ break
+ end
+ end
+ end
+ end
+
+ -- Step 2: Delete all outgoings arcs:
+ local outgoings = v.outgoings[self]
+ for i=1,#outgoings do
+ local t = outgoings[i].head
+ if t ~= v and vertices[t] then
+ local t_incomings = t.incomings[self]
+ t_incomings[v] = nil
+ for i=1,#t_incomings do
+ if t_incomings[i].tail == v then
+ table.remove (t_incomings, i)
+ break
+ end
+ end
+ end
+ end
+
+ if #incomings > 0 or #outgoings > 0 then
+ self.arcs = nil -- invalidate arcs field
+ end
+
+ -- Step 3: Reset incomings and outgoings fields
+ v.incomings[self] = {}
+ v.outgoings[self] = {}
+ end
+end
+
+
+
+
+---
+-- An arc is changed so that instead of connecting |self.tail|
+-- and |self.head|, it now connects a new |head| and |tail|. The
+-- difference to first disconnecting and then reconnecting is that all
+-- fields of the arc (other than |head| and |tail|, of course), will
+-- be ``moved along''. Reconnecting an arc in the same way as before has no
+-- effect.
+--
+-- If there is already an arc at the new position, fields of the
+-- to-be-reconnected arc overwrite fields of the original arc. This is
+-- especially dangerous with a syntactic digraph, so do not reconnect
+-- arcs of the syntactic digraph (which you should not do anyway).
+--
+-- The |arc| object may no longer be valid after a reconnect, but the
+-- operation returns the new arc object.
+--
+-- This operation needs the time of a disconnect (if necessary).
+--
+-- @param arc The original arc object
+-- @param tail The new tail vertex
+-- @param head The new head vertex
+--
+-- @return The new arc object connecting them (either newly created or
+-- already existing)
+--
+function Digraph:reconnect(arc, tail, head)
+ assert (arc and tail and head, "connect with nil parameters")
+
+ if arc.head == head and arc.tail == tail then
+ -- Nothing to be done
+ return arc
+ else
+ local new_arc = self:connect(tail, head)
+
+ for k,v in pairs(arc) do
+ if k ~= "head" and k ~= "tail" then
+ new_arc[k] = v
+ end
+ end
+
+ -- Remove old arc:
+ self:disconnect(arc.tail, arc.head)
+
+ return new_arc
+ end
+end
+
+
+
+---
+-- Collapse a set of vertices into a single vertex
+--
+-- Often, algorithms will wish to treat a whole set of vertices ``as a
+-- single vertex''. The idea is that a new vertex is then inserted
+-- into the graph, and this vertex is connected to all vertices to
+-- which any of the original vertices used to be connected.
+--
+-- The |collapse| method takes an array of to-be-collapsed vertices as
+-- well as a vertex. First, it will store references to the
+-- to-be-collapsed vertices inside the vertex. Second, we iterate over
+-- all arcs of the to-be-collapsed vertices. If this arc connects a
+-- to-be-collapsed vertex with a not-to-be-collapsed vertex, the
+-- not-to-be-collapsed vertex is connected to the collapse
+-- vertex. Additionally, the arc is stored at the vertex.
+--
+-- Note that the collapse vertex will be added to the graph if it is
+-- not already an element. The collapsed vertices will not be removed
+-- from the graph, so you must remove them yourself, if necessary.
+--
+-- A collapse vertex will store the collapsed vertices so that you can
+-- call |expand| later on to ``restore'' the vertices and arcs that
+-- were saved during a collapse. This storage is \emph{not} local to
+-- the graph in which the collapse occurred.
+--
+-- @param collapse_vertices An array of to-be-collapsed vertices
+-- @param collapse_vertex The vertex that represents the collapse. If
+-- missing, a vertex will be created automatically and added to the graph.
+-- @param vertex_fun This function is called for each to-be-collapsed
+-- vertex. The parameters are the collapse vertex and the
+-- to-be-collapsed vertex. May be |nil|.
+-- @param arc_fun This function is called whenever a new arc is added
+-- between |rep| and some other vertex. The arguments are the new arc
+-- and the original arc. May be |nil|.
+--
+-- @return The new vertex that represents the collapsed vertices.
+
+function Digraph:collapse(collapse_vertices, collapse_vertex, vertex_fun, arc_fun)
+
+
+ -- Create and add node, if necessary.
+ if not collapse_vertex then
+ collapse_vertex = Vertex.new {}
+ end
+ self:add {collapse_vertex}
+
+ -- Copy the collapse_vertices and create lookup
+ local cvs = {}
+ for i=1,#collapse_vertices do
+ local v = collapse_vertices[i]
+ cvs[i] = v
+ cvs[v] = true
+ end
+ assert (cvs[collapse_vertex] ~= true, "collapse_vertex is in collapse_vertices")
+
+ -- Connected collapse_vertex appropriately
+ local collapsed_arcs = {}
+
+ if not arc_fun then
+ arc_fun = function () end
+ end
+
+ for _,v in ipairs(cvs) do
+ if vertex_fun then
+ vertex_fun (collapse_vertex, v)
+ end
+ for _,a in ipairs(v.outgoings[self]) do
+ if cvs[a.head] ~= true then
+ arc_fun (self:connect(collapse_vertex, a.head), a)
+ collapsed_arcs[#collapsed_arcs + 1] = a
+ end
+ end
+ for _,a in ipairs(v.incomings[self]) do
+ if cvs[a.tail] ~= true then
+ arc_fun (self:connect(a.tail, collapse_vertex), a)
+ end
+ collapsed_arcs[#collapsed_arcs + 1] = a
+ end
+ end
+
+ -- Remember the old vertices.
+ collapse_vertex.collapsed_vertices = cvs
+ collapse_vertex.collapsed_arcs = collapsed_arcs
+
+ return collapse_vertex
+end
+
+
+
+---
+-- Expand a previously collapsed vertex.
+--
+-- If you have collapsed a set of vertices in a graph using
+-- |collapse|, you can expand this set once more using this method. It
+-- will add all vertices that were previously removed from the graph
+-- and will also reinstall the deleted arcs. The collapse vertex is
+-- not removed.
+--
+-- @param vertex A to-be-expanded vertex that was previously returned
+-- by |collapse|.
+-- @param vertex_fun A function that is called once for each
+-- reinserted vertex. The parameters are the collapse vertex and the
+-- reinstalled vertex. May be |nil|.
+-- @param arc_fun A function that is called once for each
+-- reinserted arc. The parameter is the arc and the |vertex|. May be |nil|.
+--
+function Digraph:expand(vertex, vertex_fun, arc_fun)
+ local cvs = assert(vertex.collapsed_vertices, "no expand information stored")
+
+ -- Add all vertices:
+ self:add(cvs)
+ if vertex_fun then
+ for _,v in ipairs(cvs) do
+ vertex_fun(vertex, v)
+ end
+ end
+
+ -- Add all arcs:
+ for _,arc in ipairs(vertex.collapsed_arcs) do
+ local new_arc = self:connect(arc.tail, arc.head)
+
+ for k,v in pairs(arc) do
+ if k ~= "head" and k ~= "tail" then
+ new_arc[k] = v
+ end
+ end
+
+ if arc_fun then
+ arc_fun(new_arc, vertex)
+ end
+ end
+end
+
+
+
+
+
+---
+-- Invokes the |sync| method for all arcs of the graph.
+--
+-- @see Arc:sync()
+--
+function Digraph:sync()
+ for _,a in ipairs(self.arcs) do
+ a:sync()
+ end
+end
+
+
+
+---
+-- Computes a string representation of this graph including all nodes
+-- and edges. The syntax of this representation is such that it can be
+-- used directly in \tikzname's |graph| syntax.
+--
+-- @return |self| as string.
+--
+function Digraph:__tostring()
+ local vstrings = {}
+ local astrings = {}
+ for i,v in ipairs(self.vertices) do
+ vstrings[i] = " " .. tostring(v) .. "[x=" .. math.floor(v.pos.x) .. "pt,y=" .. math.floor(v.pos.y) .. "pt]"
+ local out_arcs = v.outgoings[self]
+ if #out_arcs > 0 then
+ local t = {}
+ for j,a in ipairs(out_arcs) do
+ t[j] = tostring(a.head)
+ end
+ astrings[#astrings + 1] = " " .. tostring(v) .. " -> { " .. table.concat(t,", ") .. " }"
+ end
+ end
+ return "graph [id=" .. tostring(self.vertices) .. "] {\n {\n" ..
+ table.concat(vstrings, ",\n") .. "\n }; \n" ..
+ table.concat(astrings, ";\n") .. "\n}";
+end
+
+
+
+
+-- Done
+
+return Digraph
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/model/Edge.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/model/Edge.lua
new file mode 100644
index 0000000000..45cbc60f9f
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/model/Edge.lua
@@ -0,0 +1,209 @@
+-- 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$
+
+
+---
+-- An |Edge| is a ``syntactic'' connection between two
+-- vertices that represents a connection present in the syntactic
+-- digraph. Unlike an |Arc|, |Edge| objects are not controlled by the
+-- |Digraph| class. Also unlike |Arc| objects, there can be several
+-- edges between the same vertices, namely whenever several such edges
+-- are present in the syntactic digraph.
+--
+-- In detail, the relationship between arcs and edges is as follows:
+-- If there is an |Edge| between two vertices $u$ and $v$ in the
+-- syntactic digraph, there will be an |Arc| from $u$ to $v$ and the
+-- array |syntactic_edges| of this |Arc| object will contain the
+-- |Edge| object. In particular, if there are several edges between
+-- the same vertices, all of these edges will be part of the array in
+-- a single |Arc| object.
+--
+-- Edges, like arcs, are always directed from a |tail| vertex to a
+-- |head| vertex; this is true even for undirected vertices. The
+-- |tail| vertex will always be the vertex that came first in the
+-- syntactic specification of the edge, the |head| vertex is the
+-- second one. Whether
+-- an edge is directed or not depends on the |direction| of the edge, which
+-- may be one of the following:
+-- %
+-- \begin{enumerate}
+-- \item |"->"|
+-- \item |"--"|
+-- \item |"<-"|
+-- \item |"<->"|
+-- \item |"-!-"|
+-- \end{enumerate}
+--
+--
+-- @field head The head vertex of this edge.
+--
+-- @field tail The tail vertex of this edge.
+--
+-- @field event The creation |Event| of this edge.
+--
+-- @field options A table of options that contains user-defined options.
+--
+-- @field direction One of the directions named above.
+--
+-- @field path A |Path| object that describes the path of the
+-- edge. The path's coordinates are interpreted \emph{absolutely}.
+--
+-- @field generated_options This is an options array that is generated
+-- by the algorithm. When the edge is rendered later on, this array
+-- will be passed back to the display layer. The syntax is the same as
+-- for the |declare_parameter_sequence| function, see
+-- |InterfaceToAlgorithms|.
+--
+-- @field animations An array of animations, see the |animations|
+-- field of the |Vertex| class for the syntax.
+
+local Edge = {}
+Edge.__index = Edge
+
+
+-- Namespace
+
+require("pgf.gd.model").Edge = Edge
+
+
+-- Imports
+
+local Path = require "pgf.gd.model.Path"
+
+
+---
+-- Create a new edge. The |initial| parameter allows you to setup
+-- some initial values.
+--
+-- @usage
+--\begin{codeexample}[code only, tikz syntax=false]
+--local v = Edge.new { tail = v1, head = v2 }
+--\end{codeexample}
+--
+-- @param initial Values to override defaults. --
+-- @return A new edge object.
+--
+function Edge.new(values)
+ local new = {}
+ for k,v in pairs(values) do
+ new[k] = v
+ end
+ new.generated_options = new.generated_options or {}
+ new.animations = new.animations or {}
+ if not new.path then
+ local p = Path.new ()
+ p:appendMoveto(Edge.tailAnchorForEdgePath(new))
+ p:appendLineto(Edge.headAnchorForEdgePath(new))
+ new.path = p
+ end
+
+ return setmetatable(new, Edge)
+end
+
+
+
+
+---
+-- This method returns a ``coordinate factory'' that can be used as
+-- the coordinate of a |moveto| at the beginning of a path starting at
+-- the |tail| of the arc. Suppose you want to create a path starting
+-- at the tail vertex, going to the coordinate $(10,10)$ and ending at
+-- the head vertex. The trouble is that when you create the path
+-- corresponding to this route, you typically do not know where the
+-- tail vertex is going to be. In this case, you use this
+-- method to get a function that will, later on, compute the correct
+-- position of the anchor as needed.
+--
+-- Note that you typically do not use this function, but use the
+-- corresponding function of the |Arc| class. Use this function only
+-- if there are multiple edges between two vertices that need to be
+-- routed differently.
+--
+-- Here is the code you would use to create the above-mentioned path:
+-- %
+--\begin{codeexample}[code only, tikz syntax=false]
+--local a = g:connect(tail,head)
+--local e = a.syntactic_edges[1]
+--...
+--e.path = Path.new()
+--e.path:appendMoveto(e:tailAnchorForEdgePath())
+--e.path:appendLineto(10, 10)
+--e.path:appendLineto(e:headAnchorForEdgePath())
+--\end{codeexample}
+--
+-- As for the |Arc| class, you can also setup a polyline more easily:
+-- %
+--\begin{codeexample}[code only, tikz syntax=false]
+--e:setPolylinePath { Coordinate.new (10, 10) }
+--\end{codeexample}
+
+function Edge:tailAnchorForEdgePath()
+ return function ()
+ local a = self.options['tail anchor']
+ if a == "" then
+ a = "center"
+ end
+ return self.tail:anchor(a) + self.tail.pos
+ end
+end
+
+---
+-- See |Arc:tailAnchorForArcPath|.
+
+function Edge:headAnchorForEdgePath()
+ return function ()
+ local a = self.options['head anchor']
+ if a == "" then
+ a = "center"
+ end
+ return self.head:anchor(a) + self.head.pos
+ end
+end
+
+
+
+---
+-- Setup the |path| field of an edge in such a way that it corresponds
+-- to a sequence of straight line segments starting at the tail's
+-- anchor and ending at the head's anchor.
+--
+-- @param coordinates An array of |Coordinates| through which the line
+-- will go through.
+
+function Edge:setPolylinePath(coordinates)
+ local p = Path.new ()
+
+ p:appendMoveto(self:tailAnchorForEdgePath())
+
+ for _,c in ipairs(coordinates) do
+ p:appendLineto(c)
+ end
+
+ p:appendLineto(self:headAnchorForEdgePath())
+
+ self.path = p
+end
+
+
+
+--
+-- Returns a string representation of an edge. This is mainly for debugging.
+--
+-- @return The Edge as a string.
+--
+function Edge:__tostring()
+ return tostring(self.tail) .. self.direction .. tostring(self.head)
+end
+
+
+-- Done
+
+return Edge
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/model/Hyperedge.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/model/Hyperedge.lua
new file mode 100644
index 0000000000..cc265023fa
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/model/Hyperedge.lua
@@ -0,0 +1,56 @@
+-- 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$
+
+
+
+---
+-- @section subsection {Hyperedges}
+--
+-- @end
+
+
+-- Includes
+
+local declare = require "pgf.gd.interface.InterfaceToAlgorithms".declare
+
+
+---
+
+declare {
+ key = "hyper",
+ layer = -10,
+
+ summary = [["
+ A \emph{hyperedge} of a graph does not connect just two nodes, but
+ is any subset of the node set (although a normal edge is also a
+ hyperedge that happens to contain just two nodes). Internally, a
+ collection of kind |hyper| is created. Currently, there is
+ no default renderer for hyper edges.
+ "]],
+ documentation = [["
+\begin{codeexample}[code only]
+\graph {
+ % The nodes:
+ a, b, c, d;
+
+ % The edges:
+ {[hyper] a,b,c};
+ {[hyper] b,c,d};
+ {[hyper] a,c};
+ {[hyper] d}
+};
+\end{codeexample}
+ "]]
+}
+
+-- Done
+
+return Hyperedge \ No newline at end of file
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/model/Path.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/model/Path.lua
new file mode 100644
index 0000000000..cbd0f079bf
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/model/Path.lua
@@ -0,0 +1,1278 @@
+-- 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$
+
+
+---
+-- A Path models a path in the plane.
+--
+-- Following the PostScript/\textsc{pdf}/\textsc{svg} convention, a
+-- path consists of a series of path segments, each of which can be
+-- closed or not. Each path segment, in turn, consists of a series of
+-- Bézier curves and straight line segments; see
+-- Section~\ref{section-paths} for an introduction to paths in
+-- general.
+--
+-- A |Path| object is a table whose array part stores
+-- |Coordinate| objects, |strings|, and |function|s that
+-- describe the path of the edge. The following strings are allowed in
+-- this array:
+-- %
+-- \begin{itemize}
+-- \item |"moveto"| The line's path should stop at the current
+-- position and then start anew at the next coordinate in the array.
+-- \item |"lineto"| The line should continue from the current position
+-- to the next coordinate in the array.
+-- \item |"curveto"| The line should continue form the current
+-- position with a Bézier curve that is specified by the next three
+-- |Coordinate| objects (in the usual manner).
+-- \item |"closepath"| The line's path should be ``closed'' in the sense
+-- that the current subpath that was started with the most recent
+-- moveto operation should now form a closed curve.
+-- \end{itemize}
+--
+-- Instead of a |Coordinate|, a |Path| may also contain a function. In
+-- this case, the function, when called, must return the |Coordinate|
+-- that is ``meant'' by the position. This allows algorithms to
+-- add coordinates to a path that are still not fixed at the moment
+-- they are added to the path.
+
+local Path = {}
+Path.__index = Path
+
+
+-- Namespace
+
+require("pgf.gd.model").Path = Path
+
+
+-- Imports
+
+local Coordinate = require "pgf.gd.model.Coordinate"
+local Bezier = require "pgf.gd.lib.Bezier"
+
+local lib = require "pgf.gd.lib"
+
+
+-- Private function
+
+function Path.rigid (x)
+ if type(x) == "function" then
+ return x()
+ else
+ return x
+ end
+end
+
+local rigid = Path.rigid
+
+
+---
+-- Creates an empty path.
+--
+-- @param initial A table containing an array of strings and
+-- coordinates that constitute the path. Coordinates may be given as
+-- tables or as a pair of numbers. In this case, each pair of numbers
+-- is converted into one coordinate. If omitted, a new empty path
+-- is created.
+--
+-- @return A empty Path
+--
+function Path.new(initial)
+ if initial then
+ local new = {}
+ local i = 1
+ local count = 0
+ while i <= #initial do
+ local e = initial[i]
+ if type(e) == "string" then
+ assert (count == 0, "illformed path")
+ if e == "moveto" then
+ count = 1
+ elseif e == "lineto" then
+ count = 1
+ elseif e == "closepath" then
+ count = 0
+ elseif e == "curveto" then
+ count = 3
+ else
+ error ("unknown path command " .. e)
+ end
+ new[#new+1] = e
+ elseif type(e) == "number" then
+ if count == 0 then
+ new[#new+1] = "lineto"
+ else
+ count = count - 1
+ end
+ new[#new+1] = Coordinate.new(e,initial[i+1])
+ i = i + 1
+ elseif type(e) == "table" or type(e) == "function" then
+ if count == 0 then
+ new[#new+1] = "lineto"
+ else
+ count = count - 1
+ end
+ new[#new+1] = e
+ else
+ error ("invalid object on path")
+ end
+ i = i + 1
+ end
+ return setmetatable(new, Path)
+ else
+ return setmetatable({}, Path)
+ end
+end
+
+
+---
+-- Creates a copy of a path.
+--
+-- @return A copy of the path
+
+function Path:clone()
+ local new = {}
+ for _,x in ipairs(self) do
+ if type(x) == "table" then
+ new[#new+1] = x:clone()
+ else
+ new[#new+1] = x
+ end
+ end
+ return setmetatable(new, Path)
+end
+
+
+
+---
+-- Returns the path in reverse order.
+--
+-- @return A copy of the reversed path
+
+function Path:reversed()
+
+ -- First, build segments
+ local subpaths = {}
+ local subpath = {}
+
+ local function closepath ()
+ if subpath.start then
+ subpaths [#subpaths + 1] = subpath
+ subpath = {}
+ end
+ end
+
+ local prev
+ local start
+
+ local i = 1
+ while i <= #self do
+ local x = self[i]
+ if x == "lineto" then
+ subpath[#subpath+1] = {
+ action = 'lineto',
+ from = prev,
+ to = self[i+1]
+ }
+ prev = self[i+1]
+ i = i + 2
+ elseif x == "moveto" then
+ closepath()
+ prev = self[i+1]
+ start = prev
+ subpath.start = prev
+ i = i + 2
+ elseif x == "closepath" then
+ subpath [#subpath + 1] = {
+ action = "closepath",
+ from = prev,
+ to = start,
+ }
+ prev = nil
+ start = nil
+ closepath()
+ i = i + 1
+ elseif x == "curveto" then
+ local s1, s2, to = self[i+1], self[i+2], self[i+3]
+ subpath [#subpath + 1] = {
+ action = "curveto",
+ from = prev,
+ to = to,
+ support_1 = s1,
+ support_2 = s2,
+ }
+ prev = self[i+3]
+ i = i + 4
+ else
+ error ("illegal path command '" .. x .. "'")
+ end
+ end
+ closepath ()
+
+ local new = Path.new ()
+
+ for _,subpath in ipairs(subpaths) do
+ if #subpath == 0 then
+ -- A subpath that consists only of a moveto:
+ new:appendMoveto(subpath.start)
+ else
+ -- We start with a moveto to the end point:
+ new:appendMoveto(subpath[#subpath].to)
+
+ -- Now walk backwards:
+ for i=#subpath,1,-1 do
+ if subpath[i].action == "lineto" then
+ new:appendLineto(subpath[i].from)
+ elseif subpath[i].action == "closepath" then
+ new:appendLineto(subpath[i].from)
+ elseif subpath[i].action == "curveto" then
+ new:appendCurveto(subpath[i].support_2,
+ subpath[i].support_1,
+ subpath[i].from)
+ else
+ error("illegal path command")
+ end
+ end
+
+ -- Append a closepath, if necessary
+ if subpath[#subpath].action == "closepath" then
+ new:appendClosepath()
+ end
+ end
+ end
+
+ return new
+end
+
+
+---
+-- Transform all points on a path.
+--
+-- @param t A transformation, see |pgf.gd.lib.Transform|. It is
+-- applied to all |Coordinate| objects on the path.
+
+function Path:transform(t)
+ for _,c in ipairs(self) do
+ if type(c) == "table" then
+ c:apply(t)
+ end
+ end
+end
+
+
+---
+-- Shift all points on a path.
+--
+-- @param x An $x$-shift
+-- @param y A $y$-shift
+
+function Path:shift(x,y)
+ for _,c in ipairs(self) do
+ if type(c) == "table" then
+ c.x = c.x + x
+ c.y = c.y + y
+ end
+ end
+end
+
+
+---
+-- Shift by all points on a path.
+--
+-- @param x A coordinate
+
+function Path:shiftByCoordinate(x)
+ for _,c in ipairs(self) do
+ if type(c) == "table" then
+ c.x = c.x + x.x
+ c.y = c.y + x.y
+ end
+ end
+end
+
+
+---
+-- Makes the path empty.
+--
+
+function Path:clear()
+ for i=1,#self do
+ self[i] = nil
+ end
+end
+
+
+---
+-- Appends a |moveto| to the path.
+--
+-- @param x A |Coordinate| or |function| or, if the |y| parameter is
+-- not |nil|, a number that is the $x$-part of a coordinate.
+-- @param y The $y$-part of the coordinate.
+
+function Path:appendMoveto(x,y)
+ self[#self + 1] = "moveto"
+ self[#self + 1] = y and Coordinate.new(x,y) or x
+end
+
+
+---
+-- Appends a |lineto| to the path.
+--
+-- @param x A |Coordinate| or |function|, if the |y| parameter is not
+-- |nil|, a number that is the $x$-part of a coordinate.
+-- @param y The $y$-part of the coordinate.
+
+function Path:appendLineto(x,y)
+ self[#self + 1] = "lineto"
+ self[#self + 1] = y and Coordinate.new(x,y) or x
+end
+
+
+
+---
+-- Appends a |closepath| to the path.
+
+function Path:appendClosepath()
+ self[#self + 1] = "closepath"
+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
+-- sixth non-nil parameter.
+
+function Path:appendCurveto(a,b,c,d,e,f)
+ self[#self + 1] = "curveto"
+ if f then
+ self[#self + 1] = Coordinate.new(a,b)
+ self[#self + 1] = Coordinate.new(c,d)
+ self[#self + 1] = Coordinate.new(e,f)
+ else
+ self[#self + 1] = a
+ self[#self + 1] = b
+ self[#self + 1] = c
+ end
+end
+
+
+
+
+
+
+---
+-- Makes a path ``rigid'', meaning that all coordinates that are only
+-- given as functions are replaced by the values these functions
+-- yield.
+
+function Path:makeRigid()
+ for i=1,#self do
+ self[i] = rigid(self[i])
+ end
+end
+
+
+---
+-- Returns an array of all coordinates that are present in a
+-- path. This means, essentially, that all strings are filtered out.
+--
+-- @return An array of all coordinate objects on the path.
+
+function Path:coordinates()
+ local cloud = {}
+ for i=1,#self do
+ local p = self[i]
+ if type(p) == "table" then
+ cloud[#cloud + 1] = p
+ elseif type(p) == "function" then
+ cloud[#cloud + 1] = p()
+ end
+ end
+ return cloud
+end
+
+
+---
+-- Returns a bounding box of the path. This will not necessarily be
+-- the minimal bounding box in case the path contains curves because,
+-- then, the support points of the curve are used for the computation
+-- rather than the actual bounding box of the path.
+--
+-- If the path contains no coordinates, all return values are 0.
+--
+-- @return |min_x| The minimum $x$ value of the bounding box of the path
+-- @return |min_y| The minimum $y$ value
+-- @return |max_x|
+-- @return |max_y|
+-- @return |center_x| The center of the bounding box
+-- @return |center_y|
+
+function Path:boundingBox()
+ if #self > 0 then
+ local min_x, min_y = math.huge, math.huge
+ local max_x, max_y = -math.huge, -math.huge
+
+ for i=1,#self do
+ local c = rigid(self[i])
+ if type(c) == "table" then
+ local x = c.x
+ local y = c.y
+ if x < min_x then min_x = x end
+ if y < min_y then min_y = y end
+ if x > max_x then max_x = x end
+ if y > max_y then max_y = y end
+ end
+ end
+
+ if min_x ~= math.huge then
+ return min_x, min_y, max_x, max_y, (min_x+max_x) / 2, (min_y+max_y) / 2
+ end
+ end
+ return 0, 0, 0, 0, 0, 0
+end
+
+
+-- Forwards
+
+local segmentize, bb, boxes_intersect, intersect_curves
+
+local eps = 0.0001
+
+
+
+---
+-- Computes all intersections of a path with another path and returns
+-- them as an array of coordinates. The intersections will be sorted
+-- ``along the path |self|''. The implementation uses a
+-- divide-and-conquer approach that should be reasonably fast in
+-- practice.
+--
+-- @param path Another path
+--
+-- @return Array of all intersections of |path| with |self| in the
+-- order they appear on |self|. Each entry of this array is a table
+-- with the following fields:
+-- %
+-- \begin{itemize}
+-- \item |index| The index of the segment in |self| where
+-- the intersection occurs.
+-- \item |time| The ``time'' at which a point traveling along the
+-- segment from its start point to its end point.
+-- \item |point| The point itself.
+-- \end{itemize}
+
+function Path:intersectionsWith(path)
+
+ local p1 = segmentize(self)
+ local memo1 = prepare_memo(p1)
+ local p2 = segmentize(path)
+ local memo2 = prepare_memo(p2)
+
+ local intersections = {}
+
+ local function intersect_segments(i1, i2)
+
+ local s1 = p1[i1]
+ local s2 = p2[i2]
+ local r = {}
+
+ if s1.action == 'lineto' and s2.action == 'lineto' then
+ local a = s2.to.x - s2.from.x
+ local b = s1.from.x - s1.to.x
+ local c = s2.from.x - s1.from.x
+ local d = s2.to.y - s2.from.y
+ local e = s1.from.y - s1.to.y
+ local f = s2.from.y - s1.from.y
+
+ local det = a*e - b*d
+
+ if math.abs(det) > eps*eps then
+ local t, s = (c*d - a*f)/det, (b*f - e*c)/det
+
+ if t >= 0 and t<=1 and s>=0 and s <= 1 then
+ local p = s1.from:clone()
+ p:moveTowards(s1.to, t)
+ return { { time = t, point = p } }
+ end
+ end
+ elseif s1.action == 'lineto' and s2.action == 'curveto' then
+ intersect_curves (0, 1,
+ s1.from.x, s1.from.y,
+ s1.from.x*2/3+s1.to.x*1/3, s1.from.y*2/3+s1.to.y*1/3,
+ s1.from.x*1/3+s1.to.x*2/3, s1.from.y*1/3+s1.to.y*2/3,
+ s1.to.x, s1.to.y,
+ s2.from.x, s2.from.y,
+ s2.support_1.x, s2.support_1.y,
+ s2.support_2.x, s2.support_2.y,
+ s2.to.x, s2.to.y,
+ r)
+ elseif s1.action == 'curveto' and s2.action == 'lineto' then
+ intersect_curves (0, 1,
+ s1.from.x, s1.from.y,
+ s1.support_1.x, s1.support_1.y,
+ s1.support_2.x, s1.support_2.y,
+ s1.to.x, s1.to.y,
+ s2.from.x, s2.from.y,
+ s2.from.x*2/3+s2.to.x*1/3, s2.from.y*2/3+s2.to.y*1/3,
+ s2.from.x*1/3+s2.to.x*2/3, s2.from.y*1/3+s2.to.y*2/3,
+ s2.to.x, s2.to.y,
+ r)
+ else
+ intersect_curves (0, 1,
+ s1.from.x, s1.from.y,
+ s1.support_1.x, s1.support_1.y,
+ s1.support_2.x, s1.support_2.y,
+ s1.to.x, s1.to.y,
+ s2.from.x, s2.from.y,
+ s2.support_1.x, s2.support_1.y,
+ s2.support_2.x, s2.support_2.y,
+ s2.to.x, s2.to.y,
+ r)
+ end
+ return r
+ end
+
+ local function intersect (i1, j1, i2, j2)
+
+ if i1 > j1 or i2 > j2 then
+ return
+ end
+
+ local bb1 = bb(i1, j1, memo1)
+ local bb2 = bb(i2, j2, memo2)
+
+ if boxes_intersect(bb1, bb2) then
+ -- Ok, need to do something
+ if i1 == j1 and i2 == j2 then
+ local intersects = intersect_segments (i1, i2)
+ for _,t in ipairs(intersects) do
+ intersections[#intersections+1] = {
+ time = t.time,
+ index = p1[i1].path_pos,
+ point = t.point
+ }
+ end
+ elseif i1 == j1 then
+ local m2 = math.floor((i2 + j2) / 2)
+ intersect(i1, j1, i2, m2)
+ intersect(i1, j1, m2+1, j2)
+ elseif i2 == j2 then
+ local m1 = math.floor((i1 + j1) / 2)
+ intersect(i1, m1, i2, j2)
+ intersect(m1+1, j1, i2, j2)
+ else
+ local m1 = math.floor((i1 + j1) / 2)
+ local m2 = math.floor((i2 + j2) / 2)
+ intersect(i1, m1, i2, m2)
+ intersect(m1+1, j1, i2, m2)
+ intersect(i1, m1, m2+1, j2)
+ intersect(m1+1, j1, m2+1, j2)
+ end
+ end
+ end
+
+ -- Run the recursion
+ intersect(1, #p1, 1, #p2)
+
+ -- Sort
+ table.sort(intersections, function(a,b)
+ return a.index < b.index or
+ a.index == b.index and a.time < b.time
+ end)
+
+ -- Remove duplicates
+ local remains = {}
+ remains[1] = intersections[1]
+ for i=2,#intersections do
+ local next = intersections[i]
+ local prev = remains[#remains]
+ if math.abs(next.point.x - prev.point.x) + math.abs(next.point.y - prev.point.y) > eps then
+ remains[#remains+1] = next
+ end
+ end
+
+ return remains
+end
+
+
+-- Returns true if two bounding boxes intersection
+
+function boxes_intersect (bb1, bb2)
+ return (bb1.max_x >= bb2.min_x - eps*eps and
+ bb1.min_x <= bb2.max_x + eps*eps and
+ bb1.max_y >= bb2.min_y - eps*eps and
+ bb1.min_y <= bb2.max_y + eps*eps)
+end
+
+
+-- Turns a path into a sequence of segments, each being either a
+-- lineto or a curveto from some point to another point. It also sets
+-- up a memorization array for the bounding boxes.
+
+function segmentize (path)
+
+ local prev
+ local start
+ local s = {}
+
+ local i = 1
+ while i <= #path do
+ local x = path[i]
+
+ if x == "lineto" then
+ x = rigid(path[i+1])
+ s [#s + 1] = {
+ path_pos = i,
+ action = "lineto",
+ from = prev,
+ to = x,
+ bb = {
+ min_x = math.min(prev.x, x.x),
+ max_x = math.max(prev.x, x.x),
+ min_y = math.min(prev.y, x.y),
+ max_y = math.max(prev.y, x.y),
+ }
+ }
+ prev = x
+ i = i + 2
+ elseif x == "moveto" then
+ prev = rigid(path[i+1])
+ start = prev
+ i = i + 2
+ elseif x == "closepath" then
+ s [#s + 1] = {
+ path_pos = i,
+ action = "lineto",
+ from = prev,
+ to = start,
+ bb = {
+ min_x = math.min(prev.x, start.x),
+ max_x = math.max(prev.x, start.x),
+ min_y = math.min(prev.y, start.y),
+ max_y = math.max(prev.y, start.y),
+ }
+ }
+ prev = nil
+ start = nil
+ i = i + 1
+ elseif x == "curveto" then
+ local s1, s2, to = rigid(path[i+1]), rigid(path[i+2]), rigid(path[i+3])
+ s [#s + 1] = {
+ action = "curveto",
+ path_pos = i,
+ from = prev,
+ to = to,
+ support_1 = s1,
+ support_2 = s2,
+ bb = {
+ min_x = math.min(prev.x, s1.x, s2.x, to.x),
+ max_x = math.max(prev.x, s1.x, s2.x, to.x),
+ min_y = math.min(prev.y, s1.y, s2.y, to.y),
+ max_y = math.max(prev.y, s1.y, s2.y, to.y),
+ }
+ }
+ prev = path[i+3]
+ i = i + 4
+ else
+ error ("illegal path command '" .. x .. "'")
+ end
+ end
+
+ return s
+end
+
+
+function prepare_memo (s)
+
+ local memo = {}
+
+ memo.base = #s
+
+ -- Fill memo table
+ for i,e in ipairs (s) do
+ memo[i*#s + i] = e.bb
+ end
+
+ return memo
+end
+
+
+-- This function computes the bounding box of all segments between i
+-- and j (inclusively)
+
+function bb (i, j, memo)
+ local b = memo[memo.base*i + j]
+ if not b then
+ assert (i < j, "memorization table filled incorrectly")
+
+ local mid = math.floor((i+j)/2)
+ local bb1 = bb (i, mid, memo)
+ local bb2 = bb (mid+1, j, memo)
+ b = {
+ min_x = math.min(bb1.min_x, bb2.min_x),
+ max_x = math.max(bb1.max_x, bb2.max_x),
+ min_y = math.min(bb1.min_y, bb2.min_y),
+ max_y = math.max(bb1.max_y, bb2.max_y)
+ }
+ memo[memo.base*i + j] = b
+ end
+
+ return b
+end
+
+
+
+-- Intersect two Bézier curves.
+
+function intersect_curves(t0, t1,
+ c1_ax, c1_ay, c1_bx, c1_by,
+ c1_cx, c1_cy, c1_dx, c1_dy,
+ c2_ax, c2_ay, c2_bx, c2_by,
+ c2_cx, c2_cy, c2_dx, c2_dy,
+ intersections)
+
+ -- Only do something, if the bounding boxes intersect:
+ local c1_min_x = math.min(c1_ax, c1_bx, c1_cx, c1_dx)
+ local c1_max_x = math.max(c1_ax, c1_bx, c1_cx, c1_dx)
+ local c1_min_y = math.min(c1_ay, c1_by, c1_cy, c1_dy)
+ local c1_max_y = math.max(c1_ay, c1_by, c1_cy, c1_dy)
+ local c2_min_x = math.min(c2_ax, c2_bx, c2_cx, c2_dx)
+ local c2_max_x = math.max(c2_ax, c2_bx, c2_cx, c2_dx)
+ local c2_min_y = math.min(c2_ay, c2_by, c2_cy, c2_dy)
+ local c2_max_y = math.max(c2_ay, c2_by, c2_cy, c2_dy)
+
+ if c1_max_x >= c2_min_x and
+ c1_min_x <= c2_max_x and
+ c1_max_y >= c2_min_y and
+ c1_min_y <= c2_max_y then
+
+ -- Everything "near together"?
+ if c1_max_x - c1_min_x < eps and c1_max_y - c1_min_y < eps then
+
+ -- Compute intersection of lines c1_a to c1_d and c2_a to c2_d
+ local a = c2_dx - c2_ax
+ local b = c1_ax - c1_dx
+ local c = c2_ax - c1_ax
+ local d = c2_dy - c2_ay
+ local e = c1_ay - c1_dy
+ local f = c2_ay - c1_ay
+
+ local det = a*e - b*d
+ local t
+
+ t = (c*d - a*f)/det
+ if t<0 then
+ t=0
+ elseif t>1 then
+ t=1
+ end
+
+ intersections [#intersections + 1] = {
+ time = t0 + t*(t1-t0),
+ point = Coordinate.new(c1_ax + t*(c1_dx-c1_ax), c1_ay+t*(c1_dy-c1_ay))
+ }
+ else
+ -- Cut 'em in half!
+ local c1_ex, c1_ey = (c1_ax + c1_bx)/2, (c1_ay + c1_by)/2
+ local c1_fx, c1_fy = (c1_bx + c1_cx)/2, (c1_by + c1_cy)/2
+ local c1_gx, c1_gy = (c1_cx + c1_dx)/2, (c1_cy + c1_dy)/2
+
+ local c1_hx, c1_hy = (c1_ex + c1_fx)/2, (c1_ey + c1_fy)/2
+ local c1_ix, c1_iy = (c1_fx + c1_gx)/2, (c1_fy + c1_gy)/2
+
+ local c1_jx, c1_jy = (c1_hx + c1_ix)/2, (c1_hy + c1_iy)/2
+
+ local c2_ex, c2_ey = (c2_ax + c2_bx)/2, (c2_ay + c2_by)/2
+ local c2_fx, c2_fy = (c2_bx + c2_cx)/2, (c2_by + c2_cy)/2
+ local c2_gx, c2_gy = (c2_cx + c2_dx)/2, (c2_cy + c2_dy)/2
+
+ local c2_hx, c2_hy = (c2_ex + c2_fx)/2, (c2_ey + c2_fy)/2
+ local c2_ix, c2_iy = (c2_fx + c2_gx)/2, (c2_fy + c2_gy)/2
+
+ local c2_jx, c2_jy = (c2_hx + c2_ix)/2, (c2_hy + c2_iy)/2
+
+ intersect_curves (t0, (t0+t1)/2,
+ c1_ax, c1_ay, c1_ex, c1_ey, c1_hx, c1_hy, c1_jx, c1_jy,
+ c2_ax, c2_ay, c2_ex, c2_ey, c2_hx, c2_hy, c2_jx, c2_jy,
+ intersections)
+ intersect_curves (t0, (t0+t1)/2,
+ c1_ax, c1_ay, c1_ex, c1_ey, c1_hx, c1_hy, c1_jx, c1_jy,
+ c2_jx, c2_jy, c2_ix, c2_iy, c2_gx, c2_gy, c2_dx, c2_dy,
+ intersections)
+ intersect_curves ((t0+t1)/2, t1,
+ c1_jx, c1_jy, c1_ix, c1_iy, c1_gx, c1_gy, c1_dx, c1_dy,
+ c2_ax, c2_ay, c2_ex, c2_ey, c2_hx, c2_hy, c2_jx, c2_jy,
+ intersections)
+ intersect_curves ((t0+t1)/2, t1,
+ c1_jx, c1_jy, c1_ix, c1_iy, c1_gx, c1_gy, c1_dx, c1_dy,
+ c2_jx, c2_jy, c2_ix, c2_iy, c2_gx, c2_gy, c2_dx, c2_dy,
+ intersections)
+ end
+ end
+end
+
+
+---
+-- Shorten a path at the beginning. We are given the index of a
+-- segment inside the path as well as a point in time along this
+-- segment. The path is now shortened so that everything before this
+-- segment and everything in the segment before the given time is
+-- removed from the path.
+--
+-- @param index The index of a path segment.
+-- @param time A time along the specified path segment.
+
+function Path:cutAtBeginning(index, time)
+
+ local cut_path = Path:new ()
+
+ -- Ok, first, we need to find the segment *before* the current
+ -- one. Usually, this will be a moveto or a lineto, but things could
+ -- be different.
+ assert (type(self[index-1]) == "table" or type(self[index-1]) == "function",
+ "segment before intersection does not end with a coordinate")
+
+ local from = rigid(self[index-1])
+ local action = self[index]
+
+ -- Now, depending on the type of segment, we do different things:
+ if action == "lineto" then
+
+ -- Ok, compute point:
+ local to = rigid(self[index+1])
+
+ from:moveTowards(to, time)
+
+ -- Ok, this is easy: We start with a fresh moveto ...
+ cut_path[1] = "moveto"
+ cut_path[2] = from
+
+ -- ... and copy the rest
+ for i=index,#self do
+ cut_path[#cut_path+1] = self[i]
+ end
+ elseif action == "curveto" then
+
+ local to = rigid(self[index+3])
+ local s1 = rigid(self[index+1])
+ local s2 = rigid(self[index+2])
+
+ -- Now, compute the support vectors and the point at time:
+ from:moveTowards(s1, time)
+ s1:moveTowards(s2, time)
+ s2:moveTowards(to, time)
+
+ from:moveTowards(s1, time)
+ s1:moveTowards(s2, time)
+
+ from:moveTowards(s1, time)
+
+ -- Ok, this is easy: We start with a fresh moveto ...
+ cut_path[1] = "moveto"
+ cut_path[2] = from
+ cut_path[3] = "curveto"
+ cut_path[4] = s1
+ cut_path[5] = s2
+ cut_path[6] = to
+
+ -- ... and copy the rest
+ for i=index+4,#self do
+ cut_path[#cut_path+1] = self[i]
+ end
+
+ elseif action == "closepath" then
+ -- Let us find the start point:
+ local found
+ for i=index,1,-1 do
+ if self[i] == "moveto" then
+ -- Bingo:
+ found = i
+ break
+ end
+ end
+
+ assert(found, "no moveto found in path")
+
+ local to = rigid(self[found+1])
+ from:moveTowards(to,time)
+
+ cut_path[1] = "moveto"
+ cut_path[2] = from
+ cut_path[3] = "lineto"
+ cut_path[4] = to
+
+ -- ... and copy the rest
+ for i=index+1,#self do
+ cut_path[#cut_path+1] = self[i]
+ end
+ else
+ error ("wrong path operation")
+ end
+
+ -- Move cut_path back:
+ for i=1,#cut_path do
+ self[i] = cut_path[i]
+ end
+ for i=#cut_path+1,#self do
+ self[i] = nil
+ end
+end
+
+
+
+
+---
+-- Shorten a path at the end. This method works like |cutAtBeginning|,
+-- only the path is cut at the end.
+--
+-- @param index The index of a path segment.
+-- @param time A time along the specified path segment.
+
+function Path:cutAtEnd(index, time)
+
+ local cut_path = Path:new ()
+
+ -- Ok, first, we need to find the segment *before* the current
+ -- one. Usually, this will be a moveto or a lineto, but things could
+ -- be different.
+ assert (type(self[index-1]) == "table" or type(self[index-1]) == "function",
+ "segment before intersection does not end with a coordinate")
+
+ local from = rigid(self[index-1])
+ local action = self[index]
+
+ -- Now, depending on the type of segment, we do different things:
+ if action == "lineto" then
+
+ -- Ok, compute point:
+ local to = rigid(self[index+1])
+ to:moveTowards(from, 1-time)
+
+ for i=1,index do
+ cut_path[i] = self[i]
+ end
+ cut_path[index+1] = to
+
+ elseif action == "curveto" then
+
+ local s1 = rigid(self[index+1])
+ local s2 = rigid(self[index+2])
+ local to = rigid(self[index+3])
+
+ -- Now, compute the support vectors and the point at time:
+ to:moveTowards(s2, 1-time)
+ s2:moveTowards(s1, 1-time)
+ s1:moveTowards(from, 1-time)
+
+ to:moveTowards(s2, 1-time)
+ s2:moveTowards(s1, 1-time)
+
+ to:moveTowards(s2, 1-time)
+
+ -- ... and copy the rest
+ for i=1,index do
+ cut_path[i] = self[i]
+ end
+
+ cut_path[index+1] = s1
+ cut_path[index+2] = s2
+ cut_path[index+3] = to
+
+ elseif action == "closepath" then
+ -- Let us find the start point:
+ local found
+ for i=index,1,-1 do
+ if self[i] == "moveto" then
+ -- Bingo:
+ found = i
+ break
+ end
+ end
+
+ assert(found, "no moveto found in path")
+
+ local to = rigid(self[found+1]:clone())
+ to:moveTowards(from,1-time)
+
+ for i=1,index-1 do
+ cut_path[i] = self[i]
+ end
+ cut_path[index] = 'lineto'
+ cut_path[index+1] = to
+ else
+ error ("wrong path operation")
+ end
+
+ -- Move cut_path back:
+ for i=1,#cut_path do
+ self[i] = cut_path[i]
+ end
+ for i=#cut_path+1,#self do
+ self[i] = nil
+ end
+end
+
+
+
+
+---
+-- ``Pads'' the path. The idea is the following: Suppose we stroke the
+-- path with a pen whose width is twice the value |padding|. The outer
+-- edge of this stroked drawing is now a path by itself. The path will
+-- be a bit longer and ``larger''. The present function tries to
+-- compute an approximation to this resulting path.
+--
+-- The algorithm used to compute the enlarged part does not necessarily
+-- compute the precise new path. It should work correctly for polyline
+-- paths, but not for curved paths.
+--
+-- @param padding A padding distance.
+-- @return The padded path.
+--
+
+function Path:pad(padding)
+
+ local padded = self:clone()
+ padded:makeRigid()
+
+ if padding == 0 then
+ return padded
+ end
+
+ -- First, decompose the path into subpaths:
+ local subpaths = {}
+ local subpath = {}
+ local start_index = 1
+
+ local function closepath(end_index)
+ if #subpath >= 1 then
+ subpath.start_index = start_index
+ subpath.end_index = end_index
+ start_index = end_index + 1
+
+ local start = 1
+ if (subpath[#subpath] - subpath[1]):norm() < 0.01 and subpath[2] then
+ start = 2
+ subpath.skipped = subpath[1]
+ end
+ subpath[#subpath + 1] = subpath[start]
+ subpath[#subpath + 1] = subpath[start+1]
+ subpaths[#subpaths + 1] = subpath
+ subpath = {}
+ end
+ end
+
+ for i,p in ipairs(padded) do
+ if p ~= "closepath" then
+ if type(p) == "table" then
+ subpath[#subpath + 1] = p
+ end
+ else
+ closepath (i)
+ end
+ end
+ closepath(#padded)
+
+ -- Second, iterate over the subpaths:
+ for _,subpath in ipairs(subpaths) do
+ local new_coordinates = {}
+ local _,_,_,_,c_x,c_y = Coordinate.boundingBox(subpath)
+ local c = Coordinate.new(c_x,c_y)
+
+ -- Find out the orientation of the path
+ local count = 0
+ for i=1,#subpath-2 do
+ local d2 = subpath[i+1] - subpath[i]
+ local d1 = subpath[i+2] - subpath[i+1]
+
+ local diff = math.atan2(d2.y,d2.x) - math.atan2(d1.y,d1.x)
+
+ if diff < -math.pi then
+ count = count + 1
+ elseif diff > math.pi then
+ count = count - 1
+ end
+ end
+
+ for i=2,#subpath-1 do
+ local p = subpath[i]
+ local d1 = subpath[i] - subpath[i-1]
+ local d2 = subpath[i+1] - subpath[i]
+
+ local orth1 = Coordinate.new(-d1.y, d1.x)
+ local orth2 = Coordinate.new(-d2.y, d2.x)
+
+ orth1:normalize()
+ orth2:normalize()
+
+ if count < 0 then
+ orth1:scale(-1)
+ orth2:scale(-1)
+ end
+
+ -- Ok, now we want to compute the intersection of the lines
+ -- perpendicular to p + padding*orth1 and p + padding*orth2:
+
+ local det = orth1.x * orth2.y - orth1.y * orth2.x
+
+ local c
+ if math.abs(det) < 0.1 then
+ c = orth1 + orth2
+ c:scale(padding/2)
+ else
+ c = Coordinate.new (padding*(orth2.y-orth1.y)/det, padding*(orth1.x-orth2.x)/det)
+ end
+
+ new_coordinates[i] = c+p
+ end
+
+ for i=2,#subpath-1 do
+ local p = subpath[i]
+ local new_p = new_coordinates[i]
+ p.x = new_p.x
+ p.y = new_p.y
+ end
+
+ if subpath.skipped then
+ local p = subpath[1]
+ local new_p = new_coordinates[#subpath-2]
+ p.x = new_p.x
+ p.y = new_p.y
+ end
+
+ -- Now, we need to correct the curveto fields:
+ for i=subpath.start_index,subpath.end_index do
+ if self[i] == 'curveto' then
+ local from = rigid(self[i-1])
+ local s1 = rigid(self[i+1])
+ local s2 = rigid(self[i+2])
+ local to = rigid(self[i+3])
+
+ local p1x, p1y, _, _, h1x, h1y =
+ Bezier.atTime(from.x, from.y, s1.x, s1.y, s2.x, s2.y,
+ to.x, to.y, 1/3)
+
+ local p2x, p2y, _, _, _, _, h2x, h2y =
+ Bezier.atTime(from.x, from.y, s1.x, s1.y, s2.x, s2.y,
+ to.x, to.y, 2/3)
+
+ local orth1 = Coordinate.new (p1y - h1y, -(p1x - h1x))
+ orth1:normalize()
+ orth1:scale(-padding)
+
+ local orth2 = Coordinate.new (p2y - h2y, -(p2x - h2x))
+ orth2:normalize()
+ orth2:scale(padding)
+
+ if count < 0 then
+ orth1:scale(-1)
+ orth2:scale(-1)
+ end
+
+ local new_s1, new_s2 =
+ Bezier.supportsForPointsAtTime(padded[i-1],
+ Coordinate.new(p1x+orth1.x,p1y+orth1.y), 1/3,
+ Coordinate.new(p2x+orth2.x,p2y+orth2.y), 2/3,
+ padded[i+3])
+
+ padded[i+1] = new_s1
+ padded[i+2] = new_s2
+ end
+ end
+ end
+
+ return padded
+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
+-- clockwise.
+-- @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.
+--
+function Path:__tostring()
+ local r = {}
+ local i = 1
+ while i <= #self do
+ local p = self[i]
+
+ if p == "lineto" then
+ r [#r+1] = " -- " .. tostring(rigid(self[i+1]))
+ i = i + 1
+ elseif p == "moveto" then
+ r [#r+1] = " " .. tostring(rigid(self[i+1]) )
+ i = i + 1
+ elseif p == "curveto" then
+ r [#r+1] = " .. controls " .. tostring(rigid(self[i+1])) .. " and " ..
+ tostring(rigid(self[i+2])) .. " .. " .. tostring(rigid(self[i+3]))
+ i = i + 3
+ elseif p == "closepath" then
+ r [#r+1] = " -- cycle"
+ else
+ error("illegal path command")
+ end
+ i = i + 1
+ end
+ return table.concat(r)
+end
+
+
+
+-- Done
+
+return Path
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/model/Path_arced.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/model/Path_arced.lua
new file mode 100644
index 0000000000..1875b8d1a5
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/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$
+
+
+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 inadvertently 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
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/model/Vertex.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/model/Vertex.lua
new file mode 100644
index 0000000000..677ea4fbd0
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/model/Vertex.lua
@@ -0,0 +1,296 @@
+-- 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$
+
+
+---
+-- A |Vertex| instance models a node of graphs. Each |Vertex| object can be an
+-- element of any number of graphs (whereas an |Arc| object can only be an
+-- element of a single graph).
+--
+-- When a vertex is added to a digraph |g|, two tables are created in
+-- the vertex' storage: An array of incoming arcs (with respect to
+-- |g|) and an array of outgoing arcs (again, with respect to
+-- |g|). The fields are managed by the |Digraph| class and should not
+-- be modified directly.
+--
+-- Note that a |Vertex| is an abstraction of \tikzname\ nodes; indeed
+-- the objective is to ensure that, in principle, we can use them
+-- independently of \TeX. For this reason, you will not find any
+-- references to |tex| inside a |Vertex|; this information is only
+-- available in the syntactic digraph.
+--
+-- One important aspect of vertices are its anchors -- a concept well
+-- familiar for users of \tikzname, but since we need to abstract from
+-- \tikzname, a separate anchor management is available inside the
+-- graph drawing system. It works as follows:
+--
+-- First of all, every vertex has a path, which is a (typically
+-- closed) line around the vertex. The display system will pass down
+-- the vertex' path to the graph drawing system and this path will be
+-- stored as a |Path| object in the |path| field of the vertex. This
+-- path lives in a special ``local'' coordinate system, that is, all
+-- coordinates of this path should actually be considered relative to
+-- the vertex' |pos| field. Note that the path is typically, but not
+-- always, ``centered'' on the origin. A graph drawing algorithm
+-- should arrange the vertices in such a way that the origins in the
+-- path coordinate systems are aligned.
+--
+-- To illustrate the difference between the origin and the vertex
+-- center, consider a tree drawing algorithm in which a node |root| has
+-- three children |a|, |b|, and |g|. Now, if we were to simply center
+-- these three letters vertically and arrange them in a line, the
+-- letters would appear to ``jump up and down'' since the height of
+-- the three letters are quite different. A solution is to shift the
+-- letters (and, thus, the paths of the vertices) in such a way that
+-- in all three letters the baseline of the letters is exactly at the
+-- origin. Now, when a graph drawing algorithm aligns these vertices
+-- along the origins, the letters will all have the same baseline.
+--
+-- Apart from the origin, there may be other positions in the path
+-- coordinate system that are of interest -- such as the center of
+-- the vertex. As mentioned above, this need not be the origin and
+-- although a graph drawing algorithm should align the origins,
+-- \emph{edges} between vertices should head toward these vertex
+-- centers rather that toward the origins. Other points of interest
+-- might be the ``top'' of the node.
+--
+-- All points of special interest are called ``anchors''. The |anchor|
+-- method allows you to retrieve them. By default, you always have
+-- access to the |center| anchor, but other anchors may or may not be
+-- available also, see the |anchor| method for details.
+--
+-- @field pos A coordinate object that stores the position where the
+-- vertex should be placed on the canvas. The main objective of graph drawing
+-- algorithms is to update this coordinate.
+--
+-- @field name An optional string that is used as a textual representation
+-- of the node.
+--
+-- @field path The path of the vertex's shape. This is a path along
+-- the outer line resulting from stroking the vertex's original
+-- shape. For instance, if you have a quadratic shape of size 1cm and
+-- you stroke the path with a pen of 2mm thickness, this |path| field
+-- would store a path of a square of edge length 12mm.
+--
+-- @field anchors A table of anchors (in the TikZ sense). The table is
+-- indexed by the anchor names (strings) and the values are
+-- |Coordinate|s. Currently, it is only guaranteed that the |center|
+-- anchor is present. Note that the |center| anchor need not lie at
+-- the origin: A graph drawing system should align nodes relative to
+-- the origin of the path's coordinate system. However, lines going to
+-- and from the node will head towards the |center| anchor. See
+-- Section~\ref{section-gd-anchors} for details.
+--
+-- @field options A table of options that contains user-defined options.
+--
+-- @field animations An array of attribute animations for the
+-- node. When an algorithm adds entries to this array, the display
+-- layer should try to render these. The syntax is as follows: Each
+-- element in the array is a table with a field |attribute|, which must
+-- be a string like |"opacity"| or |"translate"|, a field |entries|,
+-- which must be an array to be explained in a moment, and field
+-- |options|, which must be a table of the same syntax as the
+-- |options| field. For the |entries| array, each element must be
+-- table with two field: |t| must be set to a number, representing a
+-- time in seconds, and |value|, which must be set to a value that
+-- the |attribute| should have at the given time. The entries and the
+-- options will then be interpreted as described in \pgfname's basic
+-- layer animation system, except that where a |\pgfpoint| is expected
+-- you provide a |Coordinate| and a where a path is expected you
+-- provide a |Path|.
+--
+-- @field shape A string describing the shape of the node (like |rectangle|
+-- or |circle|). Note, however, that this is more ``informative''; the
+-- actual information that is used by the graph drawing system for
+-- determining the extent of a node, its bounding box, convex hull,
+-- and line intersections is the |path| field.
+--
+-- @field kind A string describing the kind of the node. For instance, a
+-- node of type |"dummy"| does not correspond to any real node in
+-- the graph but is used by the graph drawing algorithm.
+--
+-- @field event The |Event| when this vertex was created (may be |nil|
+-- if the vertex is not part of the syntactic digraph).
+--
+-- @field incomings A table indexed by |Digraph| objects. For each
+-- digraph, the table entry is an array of all vertices from which
+-- there is an |Arc| to this vertex. This field is internal and may
+-- not only be accessed by the |Digraph| class.
+--
+-- @field outgoings Like |incomings|, but for outgoing arcs.
+--
+local Vertex = {}
+Vertex.__index = Vertex
+
+
+-- Namespace
+
+require("pgf.gd.model").Vertex = Vertex
+
+
+-- Imports
+
+local Coordinate = require "pgf.gd.model.Coordinate"
+local Path = require "pgf.gd.model.Path"
+local Storage = require "pgf.gd.lib.Storage"
+
+
+---
+-- Create a new vertex. The |initial| parameter allows you to setup
+-- some initial values.
+--
+-- @usage
+--\begin{codeexample}[code only, tikz syntax=false]
+--local v = Vertex.new { name = "hello", pos = Coordinate.new(1,1) }
+--\end{codeexample}
+--
+-- @param initial Values to override default node settings. The
+-- following are permissible:
+-- \begin{description}
+-- \item[|pos|] Initial position of the node.
+-- \item[|name|] The name of the node. It is optional to define this.
+-- \item[|path|] A |Path| object representing the vertex's hull.
+-- \item[|anchors|] A table of anchors.
+-- \item[|options|] An options table for the vertex.
+-- \item[|animations|] An array of generated animation attributes.
+-- \item[|shape|] A string describing the shape. If not given, |"none"| is used.
+-- \item[|kind|] A kind like |"node"| or |"dummy"|. If not given, |"dummy"| is used.
+-- \end{description}
+--
+-- @return A newly allocated node.
+--
+function Vertex.new(values)
+ local new = {
+ incomings = Storage.new(),
+ outgoings = Storage.new()
+ }
+ for k,v in pairs(values) do
+ new[k] = v
+ end
+ new.path = new.path or Path.new { 0, 0 }
+ new.shape = new.shape or "none"
+ new.kind = new.kind or "dummy"
+ new.pos = new.pos or Coordinate.new(0,0)
+ new.anchors = new.anchors or { center = Coordinate.new(0,0) }
+ new.animations = new.animations or {}
+ return setmetatable (new, Vertex)
+end
+
+
+
+
+---
+-- Returns a bounding box of a vertex.
+--
+-- @return |min_x| The minimum $x$ value of the bounding box of the path
+-- @return |min_y| The minimum $y$ value
+-- @return |max_x|
+-- @return |max_y|
+-- @return |center_x| The center of the bounding box
+-- @return |center_y|
+
+function Vertex:boundingBox()
+ return self.path:boundingBox()
+end
+
+
+
+local anchor_cache = Storage.new ()
+
+local directions = {
+ north = function(min_x, min_y, max_x, max_y)
+ return (min_x+max_x)/2, max_y
+ end,
+ south = function(min_x, min_y, max_x, max_y)
+ return (min_x+max_x)/2, min_y
+ end,
+ east = function(min_x, min_y, max_x, max_y)
+ return max_x, (min_y+max_y)/2
+ end,
+ west = function(min_x, min_y, max_x, max_y)
+ return min_x, (min_y+max_y)/2
+ end,
+ ["north west"] = function(min_x, min_y, max_x, max_y)
+ return min_x, max_y
+ end,
+ ["north east"] = function(min_x, min_y, max_x, max_y)
+ return max_x, max_y
+ end,
+ ["south west"] = function(min_x, min_y, max_x, max_y)
+ return min_x, min_y
+ end,
+ ["south east"] = function(min_x, min_y, max_x, max_y)
+ return max_x, min_y
+ end,
+}
+
+---
+-- Returns an anchor position in a vertex. First, we try to look
+-- the anchor up in the vertex's |anchors| table. If it is not found
+-- there, we test whether it is one of the direction strings |north|,
+-- |south east|, and so on. If so, we consider a line from the center
+-- of the node to the position on the bounding box that corresponds to
+-- the given direction (so |south east| would be the lower right
+-- corner). We intersect this line with the vertex's path and return
+-- the result. Finally, if the above fails, we try to consider the
+-- anchor as a number and return the intersection of a line starting
+-- at the vertex's center with the number as its angle and the path of
+-- the vertex.
+--
+-- @param anchor An anchor as detailed above
+-- @return A coordinate in the vertex's local coordinate system (so
+-- add the |pos| field to arrive at the actual position). If the
+-- anchor was not found, |nil| is returned
+
+function Vertex:anchor(anchor)
+ local c = self.anchors[anchor]
+ if not c then
+ local b
+ local d = directions [anchor]
+ if d then
+ b = Coordinate.new(d(self:boundingBox()))
+ else
+ local n = tonumber(anchor)
+ if n then
+ local x1, y1, x2, y2 = self:boundingBox()
+ local r = math.max(x2-x1, y2-y1)
+ b = Coordinate.new(r*math.cos(n/180*math.pi),r*math.sin(n/180*math.pi))
+ b:shiftByCoordinate(self.anchors.center)
+ end
+ end
+ if not b then
+ return
+ end
+ local p = Path.new {'moveto', self.anchors.center, 'lineto', b}
+ local intersections = p:intersectionsWith(self.path)
+ if #intersections > 0 then
+ c = intersections[1].point
+ end
+ end
+ self.anchors[anchor] = c
+ return c
+end
+
+
+
+--
+-- Returns a string representation of a vertex. This is mainly for debugging
+--
+-- @return The Arc as string.
+--
+function Vertex:__tostring()
+ return self.name or tostring(self.anchors)
+end
+
+
+-- Done
+
+return Vertex
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/model/library.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/model/library.lua
new file mode 100644
index 0000000000..ad62faa0f3
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/model/library.lua
@@ -0,0 +1,15 @@
+-- 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$
+
+
+
+-- Load declarations from:
+require "pgf.gd.model.Hyperedge"
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/ogdf.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/ogdf.lua
new file mode 100644
index 0000000000..f7905f2418
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/ogdf.lua
@@ -0,0 +1,26 @@
+-- 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$
+
+
+
+-- Imports
+
+require "pgf"
+require "pgf.gd"
+
+
+-- Declare namespace
+pgf.gd.ogdf = {}
+
+
+-- Done
+
+return pgf.gd.ogdf \ No newline at end of file
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/ogdf/library.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/ogdf/library.lua
new file mode 100644
index 0000000000..f91c75a4d6
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/ogdf/library.lua
@@ -0,0 +1,33 @@
+-- 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$
+
+
+---
+-- The Open Graph Drawing Framework (\textsc{ogdf}) is a large,
+-- powerful graph drawing system written in C++. This library enables
+-- its use inside \tikzname's graph drawing system by translating
+-- back-and-forth between Lua and C++.
+--
+-- Since C++ code is compiled and not interpreted (like Lua), in order
+-- to use the present library, you need a compiled version of the
+-- \pgfname\ interface code for the \textsc{ogdf} library
+-- (|pgf/gd/ogdf/c/ogdf_script.so|) installed correctly for your particular
+-- architecture. This is by no means trivial\dots
+--
+-- @library
+
+local ogdf
+
+
+-- Load the C++ code:
+
+require "pgf_gd_ogdf_c_ogdf_script"
+
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/pedigrees.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/pedigrees.lua
new file mode 100644
index 0000000000..95689e6379
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/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$
+
+
+local pedigrees = {}
+
+-- Declare namespace
+require("pgf.gd").pedigrees = tree
+
+
+-- Done
+
+return pedigrees \ No newline at end of file
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/pedigrees/Koerner2015.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/pedigrees/Koerner2015.lua
new file mode 100644
index 0000000000..32927530a2
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/pedigrees/Koerner2015.lua
@@ -0,0 +1,163 @@
+-- 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$
+
+
+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/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/pedigrees/library.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/pedigrees/library.lua
new file mode 100644
index 0000000000..db3d4f9cf7
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/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$
+
+
+
+---
+-- A pedigree depicts parent--mate--sibling relationships between individuals.
+--
+-- @library
+
+local pedigrees -- Library name
+
+require "pgf.gd.pedigrees.Koerner2015"
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/phylogenetics.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/phylogenetics.lua
new file mode 100644
index 0000000000..77c66de5b2
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/phylogenetics.lua
@@ -0,0 +1,21 @@
+-- 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$
+
+
+local phylogenetics = {}
+
+-- Declare namespace
+require("pgf.gd").phylogenetics = tree
+
+
+-- Done
+
+return phylogenetics \ No newline at end of file
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/phylogenetics/AuthorDefinedPhylogeny.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/phylogenetics/AuthorDefinedPhylogeny.lua
new file mode 100644
index 0000000000..04e1ca49f1
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/phylogenetics/AuthorDefinedPhylogeny.lua
@@ -0,0 +1,77 @@
+-- Copyright 2013 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$
+
+
+
+
+local AuthorDefinedPhylogeny = {}
+
+
+-- Namespace
+require("pgf.gd.phylogenetics").AuthorDefinedPhylogeny = AuthorDefinedPhylogeny
+
+-- Imports
+local InterfaceToAlgorithms = require "pgf.gd.interface.InterfaceToAlgorithms"
+local Direct = require "pgf.gd.lib.Direct"
+
+-- Shorthand:
+local declare = InterfaceToAlgorithms.declare
+
+
+---
+declare {
+ key = "phylogenetic tree by author",
+ algorithm = AuthorDefinedPhylogeny,
+ phase = "phylogenetic tree generation",
+ phase_default = true,
+
+ summary = [["
+ When this key is used, the phylogenetic tree must be specified
+ by the author (rather than being generated algorithmically).
+ "]],
+ documentation = [["
+ A spanning tree of the input graph will be computed first (it
+ must be connected, otherwise errors will result).
+ The evolutionary length of the edges must be specified through
+ the use of the |length| key for each edge.
+ "]],
+ examples = [["
+ \tikz \graph [phylogenetic tree layout] {
+ a -- {
+ b [>length=2] --[length=1] { c, d },
+ e [>length=3]
+ }
+ };
+ "]]
+}
+
+
+
+function AuthorDefinedPhylogeny:run()
+
+ local spanning_tree = self.main_algorithm.digraph.options.algorithm_phases["spanning tree computation"].new {
+ ugraph = self.main_algorithm.ugraph,
+ events = {} -- no events
+ }:run()
+
+ local phylogenetic_tree = Direct.ugraphFromDigraph(spanning_tree)
+ local lengths = self.lengths
+
+ for _,a in ipairs(phylogenetic_tree.arcs) do
+ lengths[a.tail][a.head] = a:options('length')
+ end
+
+ return phylogenetic_tree
+end
+
+
+
+return AuthorDefinedPhylogeny
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/phylogenetics/BalancedMinimumEvolution.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/phylogenetics/BalancedMinimumEvolution.lua
new file mode 100644
index 0000000000..ef40f0ee60
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/phylogenetics/BalancedMinimumEvolution.lua
@@ -0,0 +1,593 @@
+-- Copyright 2013 by Sarah Mäusle and 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$
+
+
+
+local BalancedMinimumEvolution = {}
+
+
+-- Namespace
+require("pgf.gd.phylogenetics").BalancedMinimumEvolution = BalancedMinimumEvolution
+
+-- Imports
+local InterfaceToAlgorithms = require("pgf.gd.interface.InterfaceToAlgorithms")
+local DistanceMatrix = require("pgf.gd.phylogenetics.DistanceMatrix")
+local Storage = require("pgf.gd.lib.Storage")
+local Digraph = require("pgf.gd.model.Digraph")
+local lib = require("pgf.gd.lib")
+
+-- Shorthand:
+local declare = InterfaceToAlgorithms.declare
+
+
+---
+declare {
+ key = "balanced minimum evolution",
+ algorithm = BalancedMinimumEvolution,
+ phase = "phylogenetic tree generation",
+
+ summary = [["
+ The BME (Balanced Minimum Evolution) algorithm tries to minimize
+ the total tree length.
+ "]],
+ documentation = [["
+ This algorithm is from Desper and Gascuel, \emph{Fast and
+ Accurate Phylogeny Reconstruction Algorithms Based on the
+ Minimum-Evolution Principle}, 2002. The tree is built in a way
+ that minimizes the total tree length. The leaves are inserted
+ into the tree one after another, creating new edges and new
+ nodes. After every insertion the distance matrix has to be
+ updated.
+ "]],
+ examples = [["
+ \tikz \graph [phylogenetic tree layout,
+ balanced minimum evolution,
+ grow'=right, sibling distance=0pt,
+ distance matrix={
+ 0 4 9 9 9 9 9
+ 4 0 9 9 9 9 9
+ 9 9 0 2 7 7 7
+ 9 9 2 0 7 7 7
+ 9 9 7 7 0 3 5
+ 9 9 7 7 3 0 5
+ 9 9 7 7 5 5 0}]
+ { a, b, c, d, e, f, g };
+ "]]
+}
+
+
+
+
+function BalancedMinimumEvolution:run()
+
+ self.tree = Digraph.new(self.main_algorithm.digraph)
+
+ self.distances = Storage.newTableStorage()
+
+ local vertices = self.tree.vertices
+
+ -- Sanity checks:
+ if #vertices == 2 then
+ self.tree:connect(vertices[1],vertices[2])
+ return self.tree
+ elseif #vertices > 2 then
+
+ -- Setup storages:
+ self.is_leaf = Storage.new()
+
+ -- First, build the initial distance matrix:
+ local matrix = DistanceMatrix.graphDistanceMatrix(self.tree)
+
+ -- Store distance information in the distance fields of the storages:
+ for _,u in ipairs(vertices) do
+ for _,v in ipairs(vertices) do
+ self.distances[u][v] = matrix[u][v]
+ end
+ end
+
+ -- Run BME
+ self:runBME()
+
+ -- Run postoptimizations
+ local optimization_class = self.tree.options.algorithm_phases['phylogenetic tree optimization']
+ optimization_class.new {
+ main_algorithm = self.main_algorithm,
+ tree = self.tree,
+ matrix = self.matrix,
+ distances = self.distances,
+ is_leaf = self.is_leaf,
+ }:run()
+ end
+
+ -- Finish
+ self:computeFinalLengths()
+ self:createFinalEdges()
+
+ return self.tree
+end
+
+
+
+
+-- the BME (Balanced Minimum Evolution) algorithm
+-- [DESPER and GASCUEL: Fast and Accurate Phylogeny Reconstruction
+-- Algorithms Based on the Minimum-Evolution Principle, 2002]
+--
+-- The tree is built in a way that minimizes the total tree length.
+-- The leaves are inserted into the tree one after another, creating new edges and new nodes.
+-- After every insertion the distance matrix has to be updated.
+function BalancedMinimumEvolution:runBME()
+ local g = self.tree
+ local leaves = {}
+ local is_leaf = self.is_leaf
+ local distances = self.distances
+
+ -- get user input
+ for i, vertex in ipairs (g.vertices) do
+ leaves[i] = vertex
+ is_leaf[vertex] = true
+ end
+
+ -- create the new node which will be connected to the first three leaves
+ local new_node = InterfaceToAlgorithms.createVertex(
+ self.main_algorithm,
+ {
+ name = "BMEnode"..#g.vertices+1,
+ generated_options = { { key = "phylogenetic inner node" } }
+ }
+ )
+ g:add {new_node}
+ -- set the distances of new_node to subtrees
+ local distance_1_2 = self:distance(leaves[1],leaves[2])
+ local distance_1_3 = self:distance(leaves[1],leaves[3])
+ local distance_2_3 = self:distance(leaves[2],leaves[3])
+ distances[new_node][leaves[1]] = 0.5*(distance_1_2 + distance_1_3)
+ distances[new_node][leaves[2]] = 0.5*(distance_1_2 + distance_2_3)
+ distances[new_node][leaves[3]] = 0.5*(distance_1_3 + distance_2_3)
+
+ --connect the first three leaves to the new node
+ for i = 1,3 do
+ g:connect(new_node, leaves[i])
+ g:connect(leaves[i], new_node)
+ end
+
+ for k = 4,#leaves do
+ -- compute distance from k to any subtree
+ local k_dists = Storage.newTableStorage()
+ for i = 1,k-1 do
+ -- note that the function called stores the k_dists before they are overwritten
+ self:computeAverageDistancesToAllSubtreesForK(g.vertices[i], { }, k,k_dists)
+ end
+
+ -- find the best insertion point
+ local best_arc = self:findBestEdge(g.vertices[1],nil,k_dists)
+ local head = best_arc.head
+ local tail = best_arc.tail
+
+ -- remove the old arc
+ g:disconnect(tail, head)
+ g:disconnect(head, tail)
+
+ -- create the new node
+ local new_node = InterfaceToAlgorithms.createVertex(
+ self.main_algorithm,
+ {
+ name = "BMEnode"..#g.vertices+1,
+ generated_options = {
+ { key = "phylogenetic inner node" }
+ }
+ }
+ )
+ g:add{new_node}
+
+ -- gather the vertices that will be connected to the new node...
+ local vertices_to_connect = { head, tail, leaves[k] }
+
+ -- ...and connect them
+ for _, vertex in pairs (vertices_to_connect) do
+ g:connect(new_node, vertex)
+ g:connect(vertex, new_node)
+ end
+
+ if not is_leaf[tail] then
+ distances[leaves[k]][tail] = k_dists[head][tail]
+ end
+ if not is_leaf[head] then
+ distances[leaves[k]][head] = k_dists[tail][head]
+ end
+ -- insert distances from k to subtrees into actual matrix...
+ self:setAccurateDistancesForK(new_node,nil,k,k_dists,leaves)
+
+ -- set the distance from k to the new node, which was created by inserting k into the graph
+ distances[leaves[k]][new_node] = 0.5*( self:distance(leaves[k], head) + self:distance(leaves[k],tail))
+
+ -- update the average distances
+ local values = {}
+ values.s = head -- s--u is the arc into which k has been inserted
+ values.u = tail
+ values.new_node = new_node -- the new node created by inserting k
+ self:updateAverageDistances(new_node, values,k,leaves)
+ end
+end
+
+--
+-- Updates the average distances from k to all subtrees
+--
+-- @param vertex The starting point of the recursion
+-- @param values The values needed for the recursion
+-- - s, u The nodes which span the edge into which k has been
+-- inserted
+-- - new_node The new_node which has been created to insert k
+-- - l (l-1) is the number of edges between the
+-- new_node and the current subtree Y
+--
+-- values.new_node, values.u and values.s must be set
+-- the depth first search must begin at the new node, thus vertex
+-- must be set to the newly created node
+function BalancedMinimumEvolution:updateAverageDistances(vertex, values, k, leaves)
+ local g = self.tree
+ local leaf_k = leaves[k]
+ local y, z, x
+ if not values.visited then
+ values.visited = {}
+ values.visited[leaf_k] = leaf_k -- we don't want to visit k!
+ end
+ -- there are (l-1) edges between new_node and y
+ if not values.l then values.l = 1 end
+ if not values.new_node then values.new_node = g:outgoing(leaf_k)[1].head end
+ --values.s and values.u must be set
+
+ -- the two nodes which connect the edge on which k was inserted: s,u
+
+ local new_node = values.new_node
+ local l = values.l
+ local visited = values.visited
+
+ visited[vertex] = vertex
+
+ -- computes the distances to Y{k} for all subtrees X of Z
+ function loop_over_x( x, y, values )
+ local l = values.l
+ local y1= values.y1
+
+ -- calculate distance between Y{k} and X
+ local old_distance -- the distance between Y{/k} and X needed for calculating the new distance
+ if y == new_node then -- this y didn't exist in the former tree; so use y1 (see below)
+ old_distance = self:distance(x,y1)
+ else
+ old_distance = self:distance(x,y)
+ end
+
+ local new_distance = old_distance + math.pow(2,-l) * ( self:distance(leaf_k,x) - self:distance(x,y1) )
+ self.distances[x][y] = new_distance
+ self.distances[y][x] = new_distance -- symmetric matrix
+
+ values.x_visited[x] = x
+ --go deeper to next x
+ for _, x_arc in ipairs (self.tree:outgoing(x)) do
+ if not values.x_visited[x_arc.head] then
+ local new_x = x_arc.head
+ loop_over_x( new_x, y, values )
+ end
+ end
+ end
+
+ --loop over Z's
+ for _, arc in ipairs (self.tree:outgoing(vertex)) do
+ if not visited[arc.head] then
+ -- set y1, which is the node which was pushed further away from
+ -- subtree Z by inserting k
+ if arc.head == values.s then
+ values.y1 = values.u
+ elseif arc.head == values.u then
+ values.y1 = values.s
+ else
+ assert(values.y1,"no y1 set!")
+ end
+
+ z = arc.head -- root of the subtree we're looking at
+ y = arc.tail -- the root of the subtree-complement of Z
+
+ x = z -- the first subtree of Z is Z itself
+ values.x_visited = {}
+ values.x_visited[y] = y -- we don't want to go there, as we want to stay within Z
+ loop_over_x( z,y, values ) -- visit all possible subtrees of Z
+
+ -- go to next Z
+ values.l = values.l+1 -- moving further away from the new_node
+ self:updateAverageDistances(z,values,k,leaves)
+ values.l = values.l-1 -- moving back to the new_node
+ end
+ end
+end
+
+
+--
+-- Computes the average distances of a node, which does not yet belong
+-- to the graph, to all subtrees. This is done using a depth first
+-- search
+--
+-- @param vertex The starting point of the depth first search
+-- @param values The values for the recursion
+-- - distances The table in which the distances are to be
+-- stored
+-- - outgoing_arcs The table containing the outgoing arcs
+-- of the current vertex
+--
+-- @return The average distance of the new node #k to any subtree
+-- The distances are stored as follows:
+-- example: distances[center][a]
+-- center is any vertex, thus if center is an inner vertex
+-- it has 3 neighbors a,b and c, which can all be seen as the
+-- roots of subtrees A,B,C.
+-- distances[center][a] gives us the distance of the new
+-- node k to the subtree A.
+-- if center is a leaf, it has only one neighbor, which
+-- can also be seen as the root of the subtree T\{center}
+--
+function BalancedMinimumEvolution:computeAverageDistancesToAllSubtreesForK(vertex, values, k, k_dists)
+ local is_leaf = self.is_leaf
+ local arcs = self.tree.arcs
+ local vertices = self.tree.vertices
+ local center_vertex = vertex
+ -- for every vertex a table is created, in which the distances to all
+ -- its subtrees will be stored
+
+ values.outgoing_arcs = values.outgoing_arcs or self.tree:outgoing(center_vertex)
+ for _, arc in ipairs (values.outgoing_arcs) do
+ local root = arc.head -- this vertex can be seen as the root of a subtree
+ if is_leaf[root] then -- we know the distance of k to the leaf!
+ k_dists[center_vertex][root] = self:distance(vertices[k], root)
+ else -- to compute the distance we need the root's neighboring vertices, which we can access by its outgoing arcs
+ local arc1, arc2
+ local arc_back -- the arc we came from
+ for _, next_arc in ipairs (self.tree:outgoing(root)) do
+ if next_arc.head ~= center_vertex then
+ arc1 = arc1 or next_arc
+ arc2 = next_arc
+ else
+ arc_back = next_arc
+ end
+ end
+
+ values.outgoing_arcs = { arc1, arc2, arc_back }
+
+ -- go deeper, if the distances for the next center node haven't been set yet
+ if not (k_dists[root][arc1.head] and k_dists[root][arc2.head]) then
+ self:computeAverageDistancesToAllSubtreesForK(root, values, k,k_dists)
+ end
+
+ -- set the distance between k and subtree
+ k_dists[center_vertex][root] = 1/2 * (k_dists[root][arc1.head] + k_dists[root][arc2.head])
+ end
+ end
+end
+
+
+--
+-- Sets the distances from k to subtrees
+-- In computeAverageDistancesToAllSubtreesForK the distances to ALL possible
+-- subtrees are computed. Once k is inserted many of those subtrees don't
+-- exist for k, as k is now part of them. In this function all
+-- still accurate subtrees and their distances to k are
+-- extracted.
+--
+-- @param center The vertex serving as the starting point of the depth-first search;
+-- should be the new_node
+
+function BalancedMinimumEvolution:setAccurateDistancesForK(center,visited,k,k_dists,leaves)
+ local visited = visited or {}
+ local distances = self.distances
+
+ visited[center] = center
+ local outgoings = self.tree:outgoing(center)
+ for _,arc in ipairs (outgoings) do
+ local vertex = arc.head
+ if vertex ~= leaves[k] then
+ local distance
+ -- set the distance
+ if not distances[leaves[k]][vertex] and k_dists[center] then
+ distance = k_dists[center][vertex] -- use previously calculated distance
+ distances[leaves[k]][vertex] = distance
+ distances[vertex][leaves[k]] = distance
+ end
+ -- go deeper
+ if not visited[vertex] then
+ self:setAccurateDistancesForK(vertex,visited,k,k_dists,leaves)
+ end
+ end
+ end
+end
+
+
+--
+-- Find the best edge for the insertion of leaf #k, such that the
+-- total tree length is minimized. This function uses a depth first
+-- search.
+--
+-- @param vertex The vertex where the depth first search is
+-- started; must be a leaf
+-- @param values The values needed for the recursion
+-- - visited: The vertices that already have been visited
+-- - tree_length: The current tree_length
+-- - best_arc: The current best_arc, such that the tree
+-- length is minimized
+-- - min_length: The smallest tree_length found so far
+function BalancedMinimumEvolution:findBestEdge(vertex, values, k_dists)
+ local arcs = self.tree.arcs
+ local vertices = self.tree.vertices
+ values = values or { visited = {} }
+ values.visited[vertex] = vertex
+
+ local c -- the arc we came from
+ local unvisited_arcs = {} --unvisited arcs
+ --identify arcs
+ for _, arc in ipairs (self.tree:outgoing(vertex)) do
+ if not values.visited[arc.head] then
+ unvisited_arcs[#unvisited_arcs+1] = arc
+ else
+ c = arc.head --last visited arc
+ end
+ end
+
+ for i, arc in ipairs (unvisited_arcs) do
+ local change_in_tree_length = 0
+ -- set tree length to 0 for first insertion arc
+ if not values.tree_length then
+ values.tree_length = 0
+ values.best_arc = arc
+ values.min_length = 0
+ else -- compute new tree length for the case that k is inserted into this arc
+ local b = arc.head --current arc
+ local a = unvisited_arcs[i%2+1].head -- the remaining arc
+ local k_v = vertices[k] -- the leaf to be inserted
+ change_in_tree_length = 1/4 * ( ( self:distance(a,c)
+ + k_dists[vertex][b])
+ - (self:distance(a,b)
+ + k_dists[vertex][c]) )
+ values.tree_length = values.tree_length + change_in_tree_length
+ end
+ -- if the tree length becomes shorter, this is the new best arc
+ -- for the insertion of leaf k
+ if values.tree_length < values.min_length then
+ values.best_arc = arc
+ values.min_length = values.tree_length
+ end
+
+ -- go deeper
+ self:findBestEdge(arc.head, values, k_dists)
+
+ values.tree_length = values.tree_length - change_in_tree_length
+ end
+ return values.best_arc
+end
+
+-- Calculates the total tree length
+-- This is done by adding up all the edge lengths
+--
+-- @return the tree length
+function BalancedMinimumEvolution:calculateTreeLength()
+ local vertices = self.tree.vertices
+ local sum = 0
+
+ for index, v1 in ipairs(vertices) do
+ for i = index+1,#vertices do
+ local v2 = vertices[i]
+ local dist = self.lengths[v1][v2]
+ if dist then
+ sum = sum + dist
+ end
+ end
+ end
+ return sum
+end
+
+-- generates edges for the final graph
+--
+-- throughout the process of creating the tree, arcs have been
+-- disconnected and connected, without truly creating edges. this is
+-- done in this function
+function BalancedMinimumEvolution:createFinalEdges()
+ local g = self.tree
+ local o_arcs = {} -- copy arcs since createEdge is going to modify the arcs array...
+ for _,arc in ipairs(g.arcs) do
+ if arc.tail.event.index < arc.head.event.index then
+ o_arcs[#o_arcs+1] = arc
+ end
+ end
+ for _,arc in ipairs(o_arcs) do
+ InterfaceToAlgorithms.createEdge(
+ self.main_algorithm, arc.tail, arc.head,
+ { generated_options = {
+ { key = "phylogenetic edge", value = tostring(self.lengths[arc.tail][arc.head]) }
+ }})
+ end
+end
+
+
+-- Gets the distance between two nodes as specified in their options
+-- or storage fields.
+-- Note: this function implies that the distance from a to b is the
+-- same as the distance from b to a.
+--
+-- @param a,b The nodes
+-- @return The distance between the two nodes
+
+function BalancedMinimumEvolution:distance(a, b)
+ if a == b then
+ return 0
+ else
+ local distances = self.distances
+ return distances[a][b] or distances[b][a]
+ end
+end
+
+
+--
+-- computes the final branch lengths
+--
+-- goes over all arcs and computes the final branch lengths,
+-- as neither the BME nor the BNNI main_algorithm does so.
+function BalancedMinimumEvolution:computeFinalLengths()
+ local is_leaf = self.is_leaf
+ local lengths = self.lengths
+ local g = self.tree
+ for _, arc in ipairs(g.arcs) do
+ local head = arc.head
+ local tail = arc.tail
+ local distance
+ local a,b,c,d
+ -- assert, that the length hasn't already been computed for this arc
+ if not lengths[head][tail] then
+ if not is_leaf[head] then
+ -- define subtrees a and b
+ for _, arc in ipairs (g:outgoing(head)) do
+ local subtree = arc.head
+ if subtree ~= tail then
+ a = a or subtree
+ b = subtree
+ end
+ end
+ end
+ if not is_leaf[tail] then
+ -- define subtrees c and d
+ for _, arc in ipairs (g:outgoing(tail)) do
+ local subtree = arc.head
+ if subtree ~= head then
+ c = c or subtree
+ d = subtree
+ end
+ end
+ end
+ -- compute the distance using the formula for outer or inner edges, respectively
+ if is_leaf[head] then
+ distance = 1/2 * ( self:distance(head,c)
+ + self:distance(head,d)
+ - self:distance(c,d) )
+ elseif is_leaf[tail] then
+ distance = 1/2 * ( self:distance(tail,a)
+ + self:distance(tail,b)
+ - self:distance(a,b) )
+ else --inner edge
+ distance = self:distance(head, tail)
+ -1/2 * ( self:distance(a,b)
+ + self:distance(c,d) )
+ end
+ lengths[head][tail] = distance
+ lengths[tail][head] = distance
+ end
+ end
+
+end
+
+
+
+return BalancedMinimumEvolution
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/phylogenetics/BalancedNearestNeighbourInterchange.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/phylogenetics/BalancedNearestNeighbourInterchange.lua
new file mode 100644
index 0000000000..1980afe7ad
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/phylogenetics/BalancedNearestNeighbourInterchange.lua
@@ -0,0 +1,372 @@
+-- Copyright 2013 by Sarah Mäusle and 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$
+
+
+
+local BalancedNearestNeighbourInterchange = {}
+
+
+-- Namespace
+require("pgf.gd.phylogenetics").BalancedNearestNeighbourInterchange = BalancedNearestNeighbourInterchange
+
+-- Imports
+local InterfaceToAlgorithms = require("pgf.gd.interface.InterfaceToAlgorithms")
+local DistanceMatrix = require("pgf.gd.phylogenetics.DistanceMatrix")
+local lib = require("pgf.gd.lib")
+
+-- Shorthand:
+local declare = InterfaceToAlgorithms.declare
+
+
+---
+declare {
+ key = "balanced nearest neighbour interchange",
+ algorithm = BalancedNearestNeighbourInterchange,
+ phase = "phylogenetic tree optimization",
+ phase_default = true,
+
+ summary = [["
+ The BNNI (Balanced Nearest Neighbor Interchange) is a
+ postprocessing algorithm for phylogenetic trees. It swaps two
+ distant 3-subtrees if the total tree length is reduced by doing
+ so, until no such swaps are left.
+ "]],
+ documentation = [["
+ This algorithm is from Desper and Gascuel, \emph{Fast and
+ Accurate Phylogeny Reconstruction Algorithms Based on the
+ Minimum-Evolution Principle}, 2002.
+ "]]
+}
+
+
+---
+declare {
+ key = "no phylogenetic tree optimization",
+ algorithm = { run = function(self) end },
+ phase = "phylogenetic tree optimization",
+
+ summary = [["
+ Switches off any phylogenetic tree optimization.
+ "]],
+}
+
+
+
+-- creates a binary heap, implementation as an array as described in
+-- the respective wikipedia article
+local function new_heap()
+ local heap = {}
+
+ function heap:insert(element, value)
+ local object = { element = element, value = value }
+ heap[#heap+1]= object
+
+ local i = #heap
+ local parent = math.floor(i/2)
+
+ -- sort the new object into its correct place
+ while heap[parent] and heap[parent].value < heap[i].value do
+ heap[i] = heap[parent]
+ heap[parent] = object
+ i = parent
+ parent = math.floor(i/2)
+ end
+ end
+
+ -- deletes the top element from the heap
+ function heap:remove_top_element()
+ -- replace first element with last and delete the last element
+ local element = heap[1].element
+ heap[1] = heap[#heap]
+ heap[#heap] = nil
+
+ local i = 1
+ local left_child = 2*i
+ local right_child = 2*i +1
+
+ -- sort the new top element into its correct place by swapping it
+ -- against its largest child
+ while heap[left_child] do
+ local largest_child = left_child
+ if heap[right_child] and heap[left_child].value < heap[right_child].value then
+ largest_child = right_child
+ end
+
+ if heap[largest_child].value > heap[i].value then
+ heap[largest_child], heap[i] = heap[i], heap[largest_child]
+ i = largest_child
+ left_child = 2*i
+ right_child = 2*i +1
+ else
+ return element
+ end
+ end
+ return element
+ end
+
+ return heap
+end
+
+
+-- BNNI (Balanced Nearest Neighbor Interchange)
+-- [DESPER and GASCUEL: Fast and Accurate Phylogeny Reconstruction Algorithms Based on the Minimum-Evolution Principle, 2002]
+-- swaps two distant-3 subtrees if the total tree length is reduced by doing so, until no such swaps are left
+--
+-- step 1: precomputation of all average distances between non-intersecting subtrees (already done by BME)
+-- step 2: create heap of possible swaps
+-- step 3: ( current tree with subtrees a,b,c,d: a--v-- {b, w -- {c, d}} )
+-- (a): edge (v,w) is the best swap on the heap. Remove (v,c) and (w,b)
+-- (b), (c), (d) : update the distance matrix
+-- (e): remove the edge (v,w) from the heap; check the four edges adjacent to it for new possible swaps
+-- (d): if the heap is non-empty, return to (a)
+
+function BalancedNearestNeighbourInterchange:run()
+ local g = self.tree
+ -- create a heap of possible swaps
+ local possible_swaps = new_heap()
+ -- go over all arcs, look for possible swaps and add them to the heap [step 2]
+ for _, arc in ipairs (g.arcs) do
+ self:getBestSwap(arc, possible_swaps)
+ end
+
+ -- achieve best swap and update the distance matrix, until there is
+ -- no more swap to perform
+
+ while #possible_swaps > 0 do
+ -- get the best swap and delete it from the heap
+ local swap = possible_swaps:remove_top_element() --[part of step 3 (a)]
+
+ -- Check if the indicated swap is still possible. Another swap may
+ -- have interfered.
+ if g:arc(swap.v, swap.subtree1) and g:arc(swap.w, swap.subtree2) and g:arc(swap.v, swap.w) and g:arc(swap.a, swap.v) and g:arc(swap.d, swap.w) then
+ -- insert new arcs and delete the old ones to perform the swap [part of step 3 (a)]
+
+ -- disconnect old arcs
+ g:disconnect(swap.v, swap.subtree1)
+ g:disconnect(swap.subtree1, swap.v)
+ g:disconnect(swap.w, swap.subtree2)
+ g:disconnect(swap.subtree2, swap.w)
+
+ -- connect new arcs
+ g:connect(swap.v, swap.subtree2)
+ g:connect(swap.subtree2, swap.v)
+ g:connect(swap.w, swap.subtree1)
+ g:connect(swap.subtree1, swap.w)
+
+ --update distance matrix
+ self:updateBNNI(swap)
+
+ -- update heap: check neighboring arcs for new possible swaps
+ -- [step 3 (e)]
+ self:getBestSwap(g:arc(swap.a,swap.v), possible_swaps)
+ self:getBestSwap(g:arc(swap.subtree2, swap.v), possible_swaps)
+ self:getBestSwap(g:arc(swap.d,swap.w), possible_swaps)
+ self:getBestSwap(g:arc(swap.subtree1, swap.w), possible_swaps)
+ end
+ end
+
+end
+
+
+--
+-- Gets the distance between two nodes as specified in the distances
+-- fields. Note: this function assumes that the distance from a to b
+-- is the
+-- same as the distance from b to a.
+--
+-- @param a,b The nodes
+-- @return The distance between the two nodes
+function BalancedNearestNeighbourInterchange:distance(a, b)
+ if a == b then
+ return 0
+ else
+ local distances = self.distances
+ return distances[a][b] or distances[b][a]
+ end
+end
+
+-- updates the distance matrix after a swap has been performed [step3(b),(c),(d)]
+--
+-- @param swap A table containing the information on the performed swap
+-- subtree1, subtree2: the two subtrees, which
+-- were swapped
+-- a, d: The other two subtrees bordering the
+-- swapping edge
+-- v, w : the two nodes connecting the swapping edge
+
+function BalancedNearestNeighbourInterchange:updateBNNI(swap)
+ local g = self.tree
+ local b = swap.subtree1
+ local c = swap.subtree2
+ local a = swap.a
+ local d = swap.d
+ local v = swap.v
+ local w = swap.w
+ local distances = self.distances
+
+ -- updates the distances in one of the four subtrees adjacent to the
+ -- swapping edge
+ function update_BNNI_subtree(swap, values)
+ local g = self.tree
+ local b = swap.farther
+ local c = swap.nearer
+ local a = swap.subtree
+ local v = swap.v
+ local d = swap.same
+ local w = swap.w
+
+ if not values then
+ values = {
+ visited = {[v] = v},
+ possible_ys = {v},
+ x = a,
+ y = v
+ }
+ -- if we're looking at subtrees in one of the swapped subtrees,
+ -- then need the old root (w) for the calculations
+ if swap.swapped_branch then values.possible_ys = {w} end
+ end
+ local visited = values.visited
+ local x = values.x
+ local y = values.y
+ local ys = values.possible_ys
+ local l = 0 -- number of edges between y and v
+
+ local dist_x_b = self:distance(x,b)
+ local dist_x_c = self:distance(x,c)
+ visited[x] = x --mark current x as visited
+
+ -- loop over possible y's:
+ for _, y in ipairs (ys) do
+ -- update distance [step 3(b)]
+ local distance = self:distance(x,y) - 2^(-l-2)*dist_x_b + 2^(-l-2)*dist_x_c
+
+ if y == w then y = v end -- the old distance w,x was used for the new distance calculation, but it needs to be
+ -- saved under its appropriate new name according to its new root. this case only arises when looking at x's
+ -- in one of the swapped subtrees (b or c)
+
+ distances[x][y] = distance
+ distances[y][x] = distance
+ l = l+1 -- length + 1, as the next y will be further away from v
+ end
+
+ -- update the distance between x and w (root of subtree c and d)
+ -- [step 3(c)]
+ local distance = 1/2 * (self:distance(x,b) + self:distance(x,d))
+ distances[x][w] = distance
+ distances[w][x] = distance
+
+ -- go to next possible x's
+ table.insert(ys, x) -- when we're at the next possible x, y can also be the current x
+ for _,arc in ipairs (g:outgoing(x)) do
+ if not visited[arc.head] then
+ values.x = arc.head
+ --go deeper
+ update_BNNI_subtree(swap, values)
+ end
+ end
+ end
+
+ -- name the nodes/subtrees in a general way that allows the use of the function update_BNNI_subtree
+ local update_a = {subtree = a, farther = b, nearer = c, v = v, same = d, w = w}
+ local update_b = {subtree = b, farther = a, nearer = d, v = w, same = c, w = v, swapped_branch = true}
+ local update_c = {subtree = c, farther = d, nearer = a, v = v, same = b, w = w, swapped_branch = true}
+ local update_d = {subtree = d, farther = c, nearer = b, v = w, same = a, w = v}
+
+ -- update the distances within the subtrees a,b,c,d respectively
+ update_BNNI_subtree(update_a)
+ update_BNNI_subtree(update_b)
+ update_BNNI_subtree(update_c)
+ update_BNNI_subtree(update_d)
+
+ -- update the distance between subtrees v and w [step 3 (d)]:
+ local distance = 1/4*( self:distance(a,b) + self:distance(a,d) + self:distance(c,b) + self:distance(c,d) )
+ distances[v][w] = distance
+ distances[w][v] = distance
+end
+
+
+
+-- finds the best swap across an arc and inserts it into the heap of
+-- possible swaps
+--
+-- @param arc The arc, which is to be checked for possible swaps
+-- @param heap_of_swaps The heap, containing all swaps, which
+-- improve the total tree length
+--
+-- the following data of the swap are saved:
+-- v,w = the nodes connecting the arc, across which the swap is
+-- performed
+-- subtree1,2 = the roots of the subtrees that are to be swapped
+-- a,d = the roots of the two remaining subtrees adjacent to the arc
+
+function BalancedNearestNeighbourInterchange:getBestSwap(arc, heap_of_swaps)
+ local g = self.tree
+ local possible_swaps = heap_of_swaps
+ local v = arc.tail
+ local w = arc.head
+ local is_leaf = self.is_leaf
+
+ -- only look at inner edges:
+ if not is_leaf[v] and not is_leaf[w] then
+ -- get the roots of the adjacent subtrees
+ local a, b, c, d
+ for _,outgoing in ipairs (g:outgoing(v)) do
+ local head = outgoing.head
+ if head ~= w then
+ a = a or head
+ b = head
+ end
+ end
+
+ for _,outgoing in ipairs (g:outgoing(w)) do
+ local head = outgoing.head
+ if head ~= v then
+ c = c or head
+ d = head
+ end
+ end
+
+ -- get the distances between the four subtrees
+ local a_b = self:distance(a,b)
+ local a_c = self:distance(a,c)
+ local a_d = self:distance(a,d)
+ local b_c = self:distance(b,c)
+ local b_d = self:distance(b,d)
+ local c_d = self:distance(c,d)
+
+ -- difference in total tree length between old tree (T) and new tree (T')
+ -- when nodes b and c are swapped
+ local swap1 = 1/4*(a_b + c_d - a_c - b_d )
+
+ -- difference in total tree length between old tree and new tree when nodes b and d are swapped
+ local swap2 = 1/4*(a_b + c_d - a_d - b_c)
+
+ -- choose the best swap that reduces the total tree length most (T-T' > 0)
+ if swap1 > swap2 and swap1 > 0 then
+ -- v,w = the nodes connecting the edge across which the swap is performed
+ -- subtree1 = one of the nodes to be swapped; connected to v
+ -- subtree2 = the other node to be swapped; connected to w
+ -- a = other node connected to v
+ -- d = other node connected to w
+ local swap = { v = v, w = w, subtree1 = b, subtree2 = c, a = a, d = d }
+ -- insert the swap into the heap
+ possible_swaps:insert(swap, swap1)
+ elseif swap2 > 0 then
+ local swap = { v = v, w = w, subtree1 = b, subtree2 = d, d = c, a = a }
+ possible_swaps:insert(swap, swap2)
+ end
+ end
+end
+
+
+
+return BalancedNearestNeighbourInterchange
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/phylogenetics/DistanceMatrix.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/phylogenetics/DistanceMatrix.lua
new file mode 100644
index 0000000000..a65a83da17
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/phylogenetics/DistanceMatrix.lua
@@ -0,0 +1,451 @@
+-- Copyright 2013 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$
+
+
+
+
+local DistanceMatrix = {}
+
+
+-- Imports
+local InterfaceToAlgorithms = require("pgf.gd.interface.InterfaceToAlgorithms")
+local declare = InterfaceToAlgorithms.declare
+
+
+---
+
+declare {
+ key = "distance matrix vertices",
+ type = "string",
+
+ summary = [["
+ A list of vertices that are used in the parsing of the
+ |distance matrix| key. If this key is not used at all, all
+ vertices of the graph will be used for the computation of a
+ distance matrix.
+ "]],
+
+ documentation = [["
+ The vertices must be separated by spaces and/or
+ commas. For vertices containing spaces or commas, the vertex
+ names may be surrounded by single or double quotes (as in
+ Lua). Typical examples are |a, b, c| or |"hello world", 'foo'|.
+ "]]
+}
+
+
+
+---
+
+declare {
+ key = "distance matrix",
+ type = "string",
+
+ summary = [["
+ A distance matrix specifies ``desired distances'' between
+ vertices in a graph. These distances are used, in particular, in
+ algorithms for computing phylogenetic trees.
+ "]],
+
+ documentation = [["
+ When this key is parsed, the key |distance matrix vertices| is
+ considered first. It is used to determine a list of vertices
+ for which a distance matrix is computed, see that key for
+ details. Let $n$ be the number of vertices derived from that
+ key.
+
+ The string passed to the |distance matrix| key is basically
+ a sequence of numbers that are used to fill an $n \times n$
+ matrix. This works as follows: We keep track of a \emph{current
+ position $p$} in the matrix, starting at the upper left corner
+ of the matrix. We read the numbers in the string
+ one by one, write it to the current position of the matrix, and
+ advance the current position by going right one step; if we go
+ past the right end of the matrix, we ``wrap around'' by going
+ back to the left border of the matrix, but one line down. If we
+ go past the bottom of the matrix, we start at the beginning once
+ more.
+
+ This basic behavior can be modified in different ways. First,
+ when a number is followed by a semicolon instead of a comma or a
+ space (which are the ``usual'' ways of indicating the end of a
+ number), we immediately go down to the next line. Second,
+ instead of a number you can directly provide a \emph{position}
+ in the matrix and the current position will be set to this
+ position. Such a position information is detected by a
+ greater-than sign (|>|). It must be followed by
+ %
+ \begin{itemize}
+ \item a number or a vertex name or
+ \item a number or a vertex name, a comma, and another number or
+ vertex name or
+ \item a comma and a number and a vertex name.
+ \end{itemize}
+ %
+ Examples of the respective cases are |>1|, |>a,b|, and
+ |>,5|. The semantics is as follows: In all cases, if a vertex
+ name rather than a number is given, it is converted into a
+ number (namely the index of the vertex inside the matrix). Then,
+ in the first case, the column of the current position is set to
+ the given number; in the second case, the columns is set to the
+ first number and the column is set to the second number; and in
+ the third case only the row is set to the given number. (This
+ idea is that following the |>|-sign comes a ``coordinate pair''
+ whose components are separated by a comma, but part of that pair
+ may be missing.) If a vertex name contains special symbols like
+ a space or a comma, you must surround it by single or double
+ quotation marks (as in Lua).
+
+ Once the string has been parsed completely, the matrix may be
+ filled only partially. In this case, for each missing entry
+ $(x,y)$, we try to set it to the value of the entry $(y,x)$,
+ provided that entry is set. If neither are set, the entry is set
+ to $0$.
+
+ Let us now have a look at several examples that all produce the
+ same matrix. The vertices are |a|, |b|, |c|.
+ %
+\begin{codeexample}[code only, tikz syntax=false]
+0, 1, 2
+1, 0, 3
+2, 3, 0
+\end{codeexample}
+ %
+\begin{codeexample}[code only, tikz syntax=false]
+0 1 2 1 0 3 2 3 0
+\end{codeexample}
+ %
+\begin{codeexample}[code only, tikz syntax=false]
+;
+1;
+2 3
+\end{codeexample}
+ %
+\begin{codeexample}[code only, tikz syntax=false]
+>,b 1; 2 3
+\end{codeexample}
+ %
+\begin{codeexample}[code only, tikz syntax=false]
+>b 1 2 >c 3
+\end{codeexample}
+ "]]
+}
+
+
+---
+
+declare {
+ key = "distances",
+ type = "string",
+
+ summary = [["
+ This key is used to specify the ``desired distances'' between
+ a vertex and the other vertices in a graph.
+ "]],
+
+ documentation = [["
+ This key works similar to the |distance matrix| key, only it is
+ passed to a vertex instead of to a whole graph. The syntax is
+ the same, only the notion of different ``rows'' is not
+ used. Here are some examples that all have the same effect,
+ provided the nodes are |a|, |b|, and |c|.
+ %
+\begin{codeexample}[code only, tikz syntax=false]
+0, 1, 2
+\end{codeexample}
+ %
+\begin{codeexample}[code only, tikz syntax=false]
+0 1 2
+\end{codeexample}
+ %
+\begin{codeexample}[code only, tikz syntax=false]
+>b 1 2
+\end{codeexample}
+ %
+\begin{codeexample}[code only, tikz syntax=false]
+>c 2, >b 1
+\end{codeexample}
+ "]]
+}
+
+
+
+local function to_index(s, indices)
+ if s and s ~= "" then
+ if s:sub(1,1) == '"' then
+ local _, _, m = s:find('"(.*)"')
+ return indices[InterfaceToAlgorithms.findVertexByName(m)]
+ elseif s:sub(1,1) == "'" then
+ local _, _, m = s:find("'(.*)'")
+ return indices[InterfaceToAlgorithms.findVertexByName(m)]
+ else
+ local num = tonumber(s)
+ if not num then
+ return indices[InterfaceToAlgorithms.findVertexByName(s)]
+ else
+ return num
+ end
+ end
+ end
+end
+
+local function compute_indices(vertex_string, vertices)
+ local indices = {}
+
+ if not vertex_string then
+ for i,v in ipairs(vertices) do
+ indices[i] = v
+ indices[v] = i
+ end
+ else
+ -- Ok, need to parse the vertex_string. Sigh.
+ local pos = 1
+ while pos <= #vertex_string do
+ local start = vertex_string:sub(pos,pos)
+ if not start:find("[%s,]") then
+ local _, vertex
+ if start == '"' then
+ _, pos, vertex = vertex_string:find('"(.-)"', pos)
+ elseif start == "'" then
+ _, pos, vertex = vertex_string:find("'(.-)'", pos)
+ else
+ _, pos, vertex = vertex_string:find("([^,%s'\"]*)", pos)
+ end
+ local v = assert(InterfaceToAlgorithms.findVertexByName(vertex), "unknown vertex name '" .. vertex .. "'")
+ indices [#indices + 1] = v
+ indices [v] = #indices
+ end
+ pos = pos + 1
+ end
+ end
+
+ return indices
+end
+
+
+---
+-- Compute a distance matrix based on the values of a
+-- |distance matrix| and a |distance matrix vertices|.
+--
+-- @param matrix_string A distance matrix string
+-- @param vertex_string A distance matrix vertex string
+-- @param vertices An array of all vertices in the graph.
+--
+-- @return A distance matrix. This matrix will contain both a
+-- two-dimensional array (accessed through numbers) and also a
+-- two-dimensional hash table (accessed through vertex indices). Thus,
+-- you can write both |m[1][1]| and also |m[v][v]| to access the first
+-- entry of this matrix, provided |v == vertices[1]|.
+-- @return An index vector. This is an array of the vertices
+-- identified for the |vertex_string| parameter.
+
+function DistanceMatrix.computeDistanceMatrix(matrix_string, vertex_string, vertices)
+ -- First, we create a table of the vertices we need to consider:
+ local indices = compute_indices(vertex_string, vertices)
+
+ -- Second, build matrix.
+ local n = #indices
+ local m = {}
+ for i=1,n do
+ m[i] = {}
+ end
+
+ local x = 1
+ local y = 1
+ local pos = 1
+ -- Start scanning the matrix_string
+ while pos <= #matrix_string do
+ local start = matrix_string:sub(pos,pos)
+ if not start:find("[%s,]") then
+ if start == '>' then
+ local _, parse
+ _, pos, parse = matrix_string:find(">([^%s>;]*)", pos)
+ local a, b
+ if parse:find(",") then
+ _,_,a,b = parse:find("(.*),(.*)")
+ else
+ a = parse
+ end
+ x = to_index(a, indices) or x
+ y = to_index(b, indices) or y
+ elseif start == ';' then
+ x = 1
+ y = y + 1
+ elseif start == ',' then
+ x = x + 1
+ else
+ local _, n
+ _, pos, n = matrix_string:find("([^,;%s>]*)", pos)
+ local num = assert(tonumber(n), "number expected in distance matrix")
+ m[x][y] = num
+ x = x + 1
+ -- Skip everything up to first comma:
+ _, pos = matrix_string:find("(%s*,?)", pos+1)
+ end
+ end
+ pos = pos + 1
+ if x > n then
+ x = 1
+ y = y + 1
+ end
+ if y > n then
+ y = 1
+ end
+ end
+
+ -- Fill up
+ for x=1,n do
+ for y=1,n do
+ if not m[x][y] then
+ m[x][y] = m[y][x] or 0
+ end
+ end
+ end
+
+ -- Copy to index version
+ for x=1,n do
+ local v = indices[x]
+ m[v] = {}
+ for y=1,n do
+ local u = indices[y]
+ m[v][u] = m[x][y]
+ end
+ end
+
+ return m, indices
+end
+
+
+
+
+---
+-- Compute a distance vector. See the key |distances| for details.
+--
+-- @param vector_string A distance vector string
+-- @param vertex_string A distance matrix vertex string
+-- @param vertices An array of all vertices in the graph.
+--
+-- @return A distance vector. Like a distance matrix, this vector will
+-- double indexed, once by numbers and once be vertex objects.
+-- @return An index vector. This is an array of the vertices
+-- identified for the |vertex_string| parameter.
+
+function DistanceMatrix.computeDistanceVector(vector_string, vertex_string, vertices)
+ -- First, we create a table of the vertices we need to consider:
+ local indices = compute_indices(vertex_string, vertices)
+
+ -- Second, build matrix.
+ local n = #indices
+ local m = {}
+ local x = 1
+ local pos = 1
+ -- Start scanning the vector_string
+ while pos <= #vector_string do
+ local start = vector_string:sub(pos,pos)
+ if not start:find("[%s,]") then
+ if start == '>' then
+ local _, parse
+ _, pos, parse = vector_string:find(">([^%s>;]*)", pos)
+ x = to_index(parse, indices) or x
+ elseif start == ',' then
+ x = x + 1
+ else
+ local _, n
+ _, pos, n = vector_string:find("([^,;%s>]*)", pos)
+ local num = assert(tonumber(n), "number expected in distance matrix")
+ m[x] = num
+ x = x + 1
+ -- Skip everything up to first comma:
+ _, pos = vector_string:find("(%s*,?)", pos+1)
+ end
+ end
+ pos = pos + 1
+ if x > n then
+ x = 1
+ end
+ end
+
+ -- Fill up
+ for x=1,n do
+ m[x] = m[x] or 0
+ m[indices[x]] = m[x]
+ end
+
+ return m, indices
+end
+
+
+
+---
+-- Compute a distance matrix for a graph that incorporates all
+-- information stored in the different options of the graph and the
+-- vertices.
+--
+-- @param graph A digraph object.
+--
+-- @return A distance matrix for all vertices of the graph.
+
+function DistanceMatrix.graphDistanceMatrix(digraph)
+ local vertices = digraph.vertices
+ local n = #vertices
+ local m = {}
+ for i,v in ipairs(vertices) do
+ m[i] = {}
+ m[v] = {}
+ end
+
+ local indices = {}
+ for i,v in ipairs(vertices) do
+ indices[i] = v
+ indices[v] = i
+ end
+
+ if digraph.options['distance matrix'] then
+ local sub, vers = DistanceMatrix.computeDistanceMatrix(
+ digraph.options['distance matrix'],
+ digraph.options['distance matrix vertices'],
+ vertices
+ )
+
+ for x=1,#vers do
+ for y=1,#vers do
+ m[vers[x]][vers[y]] = sub[x][y]
+ end
+ end
+ end
+
+ for i,v in ipairs(vertices) do
+ if v.options['distances'] then
+ local sub, vers = DistanceMatrix.computeDistanceVector(
+ v.options['distances'],
+ v.options['distance matrix vertices'],
+ vertices
+ )
+
+ for x=1,#vers do
+ m[vers[x]][v] = sub[x]
+ end
+ end
+ end
+
+ -- Fill up number versions:
+ for x,vx in ipairs(vertices) do
+ for y,vy in ipairs(vertices) do
+ m[x][y] = m[vx][vy]
+ end
+ end
+
+ return m
+end
+
+
+
+return DistanceMatrix
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/phylogenetics/Maeusle2012.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/phylogenetics/Maeusle2012.lua
new file mode 100644
index 0000000000..a9acc48865
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/phylogenetics/Maeusle2012.lua
@@ -0,0 +1,778 @@
+-- Copyright 2013 by Sarah Mäusle and 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$
+
+
+-- Imports
+local Digraph = require 'pgf.gd.model.Digraph'
+local Coordinate = require 'pgf.gd.model.Coordinate'
+local Path = require 'pgf.gd.model.Path'
+
+local layered = require 'pgf.gd.layered'
+
+local lib = require 'pgf.gd.lib'
+
+local declare = require("pgf.gd.interface.InterfaceToAlgorithms").declare
+
+
+
+-- Main class of this file:
+
+local Maeusle2012 = lib.class {}
+
+-- Namespace
+require("pgf.gd.phylogenetics").Maeusle2012 = Maeusle2012
+
+
+
+
+---
+declare {
+ key = "rooted rectangular phylogram",
+ algorithm = {
+ base_class = Maeusle2012,
+ run = function (self)
+ local root = self:getRoot()
+ self:setPosForRectangularLayout(root)
+ end
+ },
+ phase = "phylogenetic tree layout",
+ phase_default = true,
+
+ summary = [["
+ A rooted rectangular phylogram is...
+ "]],
+ documentation = [["
+ ...
+ "]],
+ examples = [["
+ \tikz \graph [phylogenetic tree layout,
+ rooted rectangular phylogram,
+ balanced minimum evolution,
+ distance matrix={
+ 0 4 9 9 9 9 9
+ 4 0 9 9 9 9 9
+ 9 9 0 2 7 7 7
+ 9 9 2 0 7 7 7
+ 9 9 7 7 0 3 5
+ 9 9 7 7 3 0 5
+ 9 9 7 7 5 5 0}]
+ { a, b, c, d, e, f, g };
+ "]]
+}
+
+---
+declare {
+ key = "rectangular phylogram",
+ use = { { key = "rooted rectangular phylogram" } },
+ summary = "An alias for |rooted rectangular phylogram|"
+}
+
+---
+declare {
+ key = "rooted straight phylogram",
+ algorithm = {
+ base_class = Maeusle2012,
+ run = function (self)
+ local root = self:getRoot()
+ self:setXPos(root)
+ self:setYPosForStraightLayout(root)
+ end
+ },
+ phase = "phylogenetic tree layout",
+
+ summary = [["
+ A rooted straight phylogram is...
+ "]],
+ documentation = [["
+ ...
+ "]],
+ examples = [["
+ \tikz \graph [phylogenetic tree layout,
+ rooted straight phylogram,
+ balanced minimum evolution, grow=right,
+ distance matrix={
+ 0 4 9 9 9 9 9
+ 4 0 9 9 9 9 9
+ 9 9 0 2 7 7 7
+ 9 9 2 0 7 7 7
+ 9 9 7 7 0 3 5
+ 9 9 7 7 3 0 5
+ 9 9 7 7 5 5 0}]
+ { a, b, c, d, e, f, g };
+ "]]}
+
+---
+declare {
+ key = "straight phylogram",
+ use = { { key = "rooted straight phylogram" } },
+ summary = "An alias for |rooted straight phylogram|"
+}
+
+---
+declare {
+ key = "unrooted rectangular phylogram",
+ algorithm = {
+ base_class = Maeusle2012,
+ run = function (self)
+ local root1, root2 = self:getRoot()
+ self:setPosForUnrootedRectangular(root2, root1)
+ end
+ },
+ phase = "phylogenetic tree layout",
+
+ summary = [["
+ A unrooted rectangular phylogram is...
+ "]],
+ documentation = [["
+ ...
+ "]],
+ examples = [["
+ \tikz \graph [phylogenetic tree layout,
+ unrooted rectangular phylogram,
+ balanced minimum evolution, grow=right,
+ distance matrix={
+ 0 4 9 9 9 9 9
+ 4 0 9 9 9 9 9
+ 9 9 0 2 7 7 7
+ 9 9 2 0 7 7 7
+ 9 9 7 7 0 3 5
+ 9 9 7 7 3 0 5
+ 9 9 7 7 5 5 0}]
+ { a, b, c, d, e, f, g };
+ "]]
+}
+
+---
+declare {
+ key = "unrooted straight phylogram",
+ algorithm = {
+ base_class = Maeusle2012,
+ run = function (self)
+ local root1, root2 = self:getRoot()
+ self:setPosForUnrootedStraight(root2, root1)
+ end
+ },
+ phase = "phylogenetic tree layout",
+
+ summary = [["
+ A unrooted straight phylogram is...
+ "]],
+ documentation = [["
+ ...
+ "]],
+ examples = [["
+ \tikz \graph [phylogenetic tree layout,
+ unrooted straight phylogram,
+ balanced minimum evolution, grow=right,
+ distance matrix={
+ 0 4 9 9 9 9 9
+ 4 0 9 9 9 9 9
+ 9 9 0 2 7 7 7
+ 9 9 2 0 7 7 7
+ 9 9 7 7 0 3 5
+ 9 9 7 7 3 0 5
+ 9 9 7 7 5 5 0}]
+ { a, b, c, d, e, f, g };
+ "]]
+}
+
+
+---
+declare {
+ key = "evolutionary unit length",
+ type = "length",
+ initial = "1cm",
+
+ summary = [["
+ Specifies how long a ``unit'' of evolutionary time should be on
+ paper. For instance, if two nodes in a phylogenetic tree have an
+ evolutionary distance of 3 and this length is set to |1cm|, then
+ they will be |3cm| apart in a straight-line phylogram.
+ "]],
+ documentation = [["
+ (This key used to be called |distance scaling factor|.)
+ "]],
+}
+
+
+
+--
+-- Gets the edge length between two nodes
+--
+-- @param vertex1, vertex2 The two nodes
+--
+-- @return The length of the edge between the two nodes
+function Maeusle2012:edgeLength(vertex1, vertex2)
+ return self.lengths[vertex1][vertex2]
+end
+
+
+-- Sets the x and y coordinates for all nodes, using a depth first
+-- search
+--
+-- @param vertex The starting point; should usually be the root
+-- @param values Values needed for the recursion
+-- @param vertex2 A node that will not be visited; this parameter should only be set
+-- for an unrooted layout to ensure that only the half of the tree is set.
+function Maeusle2012:setPosForRectangularLayout(vertex, values, vertex2)
+ local arcs = self.tree.arcs
+ local vertices = self.tree.vertices
+ local adjusted_bb = self.main_algorithm.adjusted_bb
+
+ values = values or {
+ length = 0, -- current path length
+ visited = {}, -- all nodes that have already been visited
+ leaves = {}, -- all leaves from left to right
+ }
+
+ local vertex_is_leaf = true
+ values.visited[vertex] = true
+
+ local children = {} -- a table containing all children of the
+ -- current vertex (for the later determination of inner vertices
+ -- x-positions)
+
+
+ for _, arc in ipairs (self.tree:outgoing(vertex)) do
+ if not values.visited[arc.head] and arc.head ~= vertex2 then
+ -- if arc.head hasn't been visited, the current vertex cannot be a leaf
+ vertex_is_leaf = false
+ local arc_length = self:edgeLength(vertex, arc.head)
+
+ values.length = values.length + arc_length
+
+ -- go deeper
+ self:setPosForRectangularLayout(arc.head, values, vertex2)
+
+ -- get the children of the current vertex
+ children[#children+1] = arc.head
+
+ values.length = values.length - arc_length
+ end
+ end
+
+ if vertex_is_leaf then
+ -- subtract layer_pre, thus the leaf itself is NOT part of the
+ -- edge length
+ vertex.pos.y = - adjusted_bb[vertex].layer_pre
+
+ values.leaves[#values.leaves+1] = vertex
+
+ -- x coordinate:
+ -- the x coordinates of the leaves are the first to be set; the
+ -- first leave stays at x = 0, the x coordinates for the other
+ -- leaves is computed with help of the ideal_sibling_distance
+ -- function
+ if #values.leaves > 1 then
+ local left_sibling = values.leaves[#values.leaves-1]
+ local ideal_distance = layered.ideal_sibling_distance(adjusted_bb, self.tree, vertex, left_sibling )
+ vertex.pos.x = left_sibling.pos.x + ideal_distance
+ end
+
+ else -- the vertex is an inner node
+ -- the x position of an inner vertex is at the center of its children.
+
+ -- determine the outer children
+ local left_child = children[1]
+ local right_child = left_child
+ for _, child in ipairs(children) do
+ if child.pos.x < left_child.pos.x then left_child = child end
+ if child.pos.x > right_child.pos.x then right_child = child end
+ end
+
+ -- position between child with highest and child with lowest x-value,
+ -- if number of children is even
+ local index_of_middle_child = math.ceil(#children/2)
+ local even = #children/2 == index_of_middle_child
+
+ if even then
+ vertex.pos.x = (left_child.pos.x + right_child.pos.x) / 2
+ index_of_middle_child = 0
+ else -- if number of children is odd, position above the middle child
+ vertex.pos.x = children[index_of_middle_child].pos.x
+ table.remove(children, index_of_middle_child) -- don't bend the edge to this node, as it it above it anyway
+ end
+ end
+
+ -- set the node's y-coordinate, using the calculated length
+ -- and a scaling factor
+ vertex.pos.y = vertex.pos.y + (values.length * self.tree.options['evolutionary unit length'])
+
+ -- if this is the second subtree to be set of an unrooted tree, have
+ -- it grow in the other direction
+ if values.second_subtree then
+ vertex.pos.y = -vertex.pos.y
+ end
+
+ -- bend the edges for the rectangular layout
+ for i,child in ipairs(children) do
+ self:bendEdge90Degree(child, vertex)
+ end
+
+ return values
+end
+
+
+-- Sets only the x-positions of all nodes using a depth-first search.
+-- This is necessary for straight-edge layouts.
+--
+-- @param vertex The starting point of the depth-first search; should usually be the root
+-- @param values Values needed for the recursion
+-- @param vertex2 A node that will not be visited; this parameter should only be set
+-- for an unrooted layout to ensure that only the half of the tree is set.
+function Maeusle2012:setXPos(vertex, values, vertex2)
+ local arcs = self.tree.arcs
+ local vertices = self.tree.vertices
+ if not values then
+ values = {
+ visited = {}, -- all nodes that have already been visited
+ leaves = {}, -- all leaves from left to right
+ }
+ end
+
+ local vertex_is_leaf = true
+ values.visited[vertex] = true
+ local children = {} -- a table containing all children of the current vertex (for the later determination of inner vertices x-positions)
+
+ for _, arc in ipairs (self.tree:outgoing(vertex)) do
+ if not values.visited[arc.head] and arc.head ~= vertex2 then
+ -- if arc.head hasn't been visited, the current vertex cannot be a leaf
+ vertex_is_leaf = false
+
+ -- go deeper
+ self:setXPos(arc.head, values, vertex2)
+
+ -- get the children of the current vertex
+ table.insert(children, arc.head)
+ end
+ end
+
+ -- set the x-position of a leaf
+ if vertex_is_leaf then
+
+ table.insert(values.leaves, vertex)
+
+ if #values.leaves > 1 then
+ local left_sibling = values.leaves[#values.leaves-1]
+ local ideal_distance = layered.ideal_sibling_distance(self.main_algorithm.adjusted_bb, self.tree, vertex, left_sibling )
+ vertex.pos.x = left_sibling.pos.x + ideal_distance
+ end
+
+ -- set x position of an inner node, which is at the center of its
+ -- children
+ else
+ -- determine the outer children
+ local left_child = children[1]
+ local right_child = left_child
+ for _, child in ipairs(children) do
+ if child.pos.x < left_child.pos.x then left_child = child end
+ if child.pos.x > right_child.pos.x then right_child = child end
+ end
+
+ -- position between child with highest and child with lowest x-value,
+ -- if number of children is even
+ local index_of_middle_child = math.ceil(#children/2)
+ local even = #children/2 == index_of_middle_child
+
+ if even then
+ vertex.pos.x = (left_child.pos.x + right_child.pos.x) / 2
+ else -- if number of children is odd, position above the middle child
+ vertex.pos.x = children[index_of_middle_child].pos.x
+ end
+ end
+ return values
+end
+
+
+--
+-- Sets only the y-positions of all nodes using a depth-first search.
+-- This is needed for a straight-edge layout, as the x-positions have
+-- to bet first so that the y-coordinates can be calculated correctly
+-- here.
+--
+-- @param vertex1 The starting point of the depth-first search
+-- @param values Values needed for the recursion
+-- @param vertex2 For unrooted layout only: The root of the second subtree.
+-- This node and all its children will not be visited.
+function Maeusle2012:setYPosForStraightLayout(vertex, values, vertex2)
+ local arcs = self.tree.arcs
+ local vertices = self.tree.vertices
+ local adjusted_bb = self.main_algorithm.adjusted_bb
+
+ values = values or {
+ length = 0, -- current path length
+ visited = {}, -- all nodes that have already been visited
+ leaves = {}, -- all leaves from left to right
+ }
+
+ local vertex_is_leaf = true
+ values.visited[vertex] = true
+ local children = {} -- a table containing all children of the current vertex (for the later determination of inner vertices x-positions)
+
+ for _, arc in ipairs (self.tree:outgoing(vertex)) do
+ if not values.visited[arc.head] and arc.head ~= vertex2 then
+ -- if arc.head hasn't been visited, the current vertex cannot be a leaf
+ vertex_is_leaf = false
+
+ -- calculate the arc length with the help of the Pythagorean
+ -- theorem
+ local a
+ local l = self:edgeLength(vertex, arc.head) * self.tree.options['evolutionary unit length']
+ local b = math.abs(vertex.pos.x - arc.head.pos.x)
+ if b > l then
+ a = 0
+ else
+ a = math.sqrt(l^2-b^2)
+ end
+ local arc_length = a
+
+
+ values.length = values.length + arc_length
+
+ -- go deeper
+ self:setYPosForStraightLayout(arc.head, values, vertex2)
+
+ -- get the children of the current vertex
+ table.insert(children, arc.head)
+
+ values.length = values.length - arc_length
+ end
+ end
+
+ if vertex_is_leaf then
+ -- subtract layer_pre, thus the leaf itself is NOT part of the
+ -- edge length
+ vertex.pos.y = - adjusted_bb[vertex].layer_pre
+
+ table.insert(values.leaves, vertex)
+ end
+
+ -- set the node's y-coordinate, using the calculated length
+ vertex.pos.y = vertex.pos.y + values.length
+
+ -- if this is the second subtree to be set of an unrooted tree, have
+ -- it grow in the other direction
+ if values.second_subtree then vertex.pos.y = -vertex.pos.y end
+end
+
+--
+-- Correct the x-positions in the unrooted layout for a more aesthetic result
+--
+-- If the roots of the two subtrees have different x-positions, this is corrected
+-- by shifting the x-positions of all nodes in one subtree by that difference.
+--
+-- @param vertex1 The root of the first subtree
+-- @param vertex2 The root of the second subtree.
+function Maeusle2012:correctXPos(vertex1, vertex2, straight)
+
+ -- correct the x-positions
+ --
+ -- @param vertex Starting point of the depth-first search
+ -- @param values Values needed for the recursion
+ -- @param vertex2 The root of the subtree that will not be visited
+ local function x_correction(vertex, values, vertex2)
+ values.visited[vertex] = true
+ local children = {}
+
+ for _, arc in ipairs (self.tree:outgoing(vertex)) do
+ if not values.visited[arc.head] and arc.head ~= vertex2 then
+
+ table.insert(children, arc.head)
+ x_correction(arc.head, values, vertex2)
+ end
+ end
+
+ vertex.pos.x = vertex.pos.x + values.diff
+ if not straight then
+ for i,child in ipairs(children) do
+ self:bendEdge90Degree(child, vertex)
+ end
+ end
+
+ return values
+ end
+
+ -- compute the difference of the x-positions of the two subtrees'
+ -- roots
+ local diff = vertex1.pos.x - vertex2.pos.x
+ local values = { visited = {} }
+ if diff < 0 then
+ values.diff = - diff
+ x_correction(vertex1, values, vertex2)
+ elseif diff > 0 then
+ values.diff = diff
+ x_correction(vertex2, values, vertex1)
+ end
+end
+
+
+--
+-- Sets the x- and y-positions of the vertices in an unrooted layout
+--
+-- This is done using the function for setting the positions for a rooted layout:
+-- Two neighboring vertices are chosen as roots; one half of the tree
+-- is drawn in one direction, the other half 180° to the other
+-- direction.
+--
+-- @param vertex1, vertex2: The vertices functioning as roots
+function Maeusle2012:setPosForUnrootedRectangular(vertex1, vertex2)
+ -- set positions for first half of the tree...
+ self:setPosForRectangularLayout(vertex2,false,vertex1)
+ local vals={
+ length = self:edgeLength(vertex1, vertex2), -- the length between the two roots
+ visited={},
+ leaves={},
+ path={},
+ second_subtree = true
+ }
+ -- ... and for the second half.
+ self:setPosForRectangularLayout(vertex1,vals,vertex2)
+ -- if the two roots have different x-values, correct the x-positions for nicer layout
+ self:correctXPos(vertex1, vertex2, false)
+end
+
+
+--
+-- Sets the x- and y-positions of the vertices in an unrooted straight layout
+--
+-- This is done using the function for setting the positions for a rooted straight layout:
+-- Two neighboring vertices are chosen as roots; one half of the tree
+-- is drawn in one direction, the other half 180° to the other
+-- direction.
+--
+-- @param vertex1, vertex2: The vertices functioning as roots
+function Maeusle2012:setPosForUnrootedStraight(vertex1, vertex2)
+ -- first set the x-positions of the two subtrees...
+ local vals = {visited = {}, leaves = {} }
+ self:setXPos(vertex2, vals, vertex1)
+ self:setXPos(vertex1, vals, vertex2)
+
+ -- ... and then the y-positions
+ self:setYPosForStraightLayout(vertex2, false, vertex1)
+ local vals={
+ length = self:edgeLength(vertex1, vertex2) * self.tree.options['evolutionary unit length'],
+ visited={},
+ leaves={},
+ path={},
+ second_subtree = true
+ }
+ self:setYPosForStraightLayout(vertex1, vals, vertex2)
+
+ -- if the two roots have different x-values, correct the x-positions for nicer layout
+ -- as the length between the roots of the two subtrees is set to the calculated value,
+ -- this step is mandatory for the unrooted, straight layout
+ self:correctXPos(vertex1, vertex2, true)
+end
+
+
+
+-- Bends the arc between two nodes by 90 degree by updating the arc's
+-- path
+--
+-- @param head The head of the arc
+-- @param tail The tail of the arc
+function Maeusle2012:bendEdge90Degree(head, tail)
+ local arc = self.tree:arc(tail,head)
+ local syntactic_tail = arc:syntacticTailAndHead()
+ arc:setPolylinePath { Coordinate.new(head.pos.x, tail.pos.y) }
+end
+
+
+
+-- Finds the longest path in a graph
+--
+-- @ return A table containing the path (an array of nodes) and the
+-- path length
+function Maeusle2012:findLongestPath()
+ local starting_point = self.tree.vertices[1] -- begin at any vertex
+ -- get the path lengths from the starting point to all leaves:
+ local paths_to_leaves = self:getPathLengthsToLeaves(starting_point)
+ local path_lengths = paths_to_leaves.path_lengths
+ local paths = paths_to_leaves.paths
+
+ -- looks for the longest path and identifies its end-point
+ local function find_head_of_longest_path(path_lengths, paths)
+ local longest_path
+ local node
+ -- to make sure that the same path is chosen every time, we go over all vertices with "ipairs"; if we would go over path_lengths directly, we could only use "pairs"
+ for _, vertex in ipairs(self.tree.vertices) do
+ local path_length = path_lengths[vertex]
+ if path_length then
+ -- choose longest path. if two paths have the same length, take the path with more nodes
+ if not longest_path or path_length > longest_path or (path_length == longest_path and #paths[vertex]>#paths[node]) then
+ longest_path = path_length
+ node = vertex
+ end
+ end
+ end
+ return node
+ end
+
+ -- find the longest path leading away from the starting point and identify
+ -- the leaf it leads to. Use that leaf as the tail for the next path
+ -- search
+ local tail = find_head_of_longest_path(path_lengths, paths)
+ paths_to_leaves = self:getPathLengthsToLeaves(tail) -- gets new path information
+ -- paths_to leaves now has all paths starting at vertex "tail"; one of these paths is the
+ -- longest (globally)
+ path_lengths = paths_to_leaves.path_lengths
+ paths = paths_to_leaves.paths
+ local head = find_head_of_longest_path(path_lengths, paths)
+
+ local path_information =
+ { path = paths_to_leaves.paths[head], -- longest path
+ length = path_lengths[head] } -- length of that path
+
+ return path_information
+end
+
+
+-- a depth first search for getting all path lengths from a
+-- starting point to all leaves
+--
+-- @param vertex The vertex where the search is to start
+-- @param values Table of values needed for the recursive computation
+--
+-- @return A table containing:
+-- a table of the leaves with corresponding path lengths
+-- and a table containing the path to each leaf (an array of
+-- nodes)
+function Maeusle2012:getPathLengthsToLeaves(vertex, values)
+ local arcs = self.tree.arcs
+ local vertices = self.tree.vertices
+ if not values then
+ values = {
+ paths = {}, -- all paths we've found so far
+ path_lengths = {}, -- all path lengths that have so far been computed
+ length = 0, -- current path length
+ visited = {}, -- all nodes that have already been visited
+ path = {}, -- the current path we're on
+ leaves = {} -- all leaves from left to right
+ }
+ table.insert(values.path,vertex)
+ end
+
+ local vertex_is_leaf = true
+ values.visited[vertex] = true
+
+ for _, arc in ipairs (self.tree:outgoing(vertex)) do
+ if not values.visited[arc.head] then
+ -- the current vertex is not a leaf! note: if the starting vertex is a leaf, vertex_is_leaf
+ -- will be set to 'false' for it anyway. as we're not interested in the distance
+ -- of the starting vertex to itself, this is fine.
+ vertex_is_leaf = false
+ local arc_length = self.lengths[vertex][arc.head]
+ values.length = values.length + arc_length
+
+ -- add arc.head to path...
+ table.insert(values.path,arc.head)
+
+ -- ... and go down that path
+ self:getPathLengthsToLeaves(arc.head, values)
+
+ -- remove arc.head again to go a different path
+ table.remove(values.path)
+ values.length = values.length - arc_length
+ end
+ end
+
+ if vertex_is_leaf then -- we store the information gained on the path to this leaf
+ values.path_lengths[vertex] = values.length
+ values.paths[vertex] = {}
+ table.insert(values.leaves, vertex)
+ for i,k in pairs(values.path) do
+ values.paths[vertex][i] = k
+ end
+ end
+ -- the path_lengths and the paths are stored in one table and
+ -- returned together
+ local path_information =
+ { path_lengths = values.path_lengths,
+ paths = values.paths,
+ leaves = values.leaves }
+ return path_information
+end
+
+
+-- Gets the root of a tree
+-- checks whether a tree is already rooted, if not, computeCenterOfPath() is
+-- called, which defines a node in the center of the graph as the root
+--
+-- @return The root
+function Maeusle2012:getRoot()
+ -- check whether a root exists (vertex with degree 2)
+ local root = lib.find (self.tree.vertices, function(v) return #self.tree:outgoing(v) == 2 end)
+ if root then
+ return root, self.tree:outgoing(root)[1].head
+ else
+ return self:computeCenterOfPath()
+ end
+end
+
+
+--
+-- @return The newly computed root and its nearest neighbor
+function Maeusle2012:computeCenterOfPath()
+ local longest_path = self:findLongestPath()
+ local path = longest_path.path
+ local root, neighbor_of_root
+
+ local length = 0 --length between first vertex on the path and the current vertex we're looking at
+ for i = 1, #path-1 do
+ local node1 = path[i]
+ local node2 = path[i+1]
+ local node3 = path[i+2]
+
+ local dist_node_1_2, dist_node_2_3 --distances between node1 and node2, and node2 and node3
+ dist_node_1_2 = self:edgeLength(node1, node2)
+ if node3 then dist_node_2_3 = self:edgeLength(node2, node3) end
+ length = length + dist_node_1_2 -- length between first vertex on the path and current node2
+
+ if length == longest_path.length/2 then
+ root = node2 -- if there is a node exactly at the half of the path, use this node as root
+
+ -- and find nearest neighbor of the root
+ if node3 == nil or dist_node_1_2 < dist_node_2_3 then -- neu 3.8
+ neighbor_of_root = node1
+ else
+ neighbor_of_root = node3
+ end
+ break
+
+ elseif length > longest_path.length/2 then
+ -- else find node closest to the center of the path and use it as the root;
+ local node2_length = math.abs(longest_path.length/2 - length)
+ local node1_length = math.abs(longest_path.length/2 - (length - dist_node_1_2))
+ if node2_length < node1_length then
+ root = node2
+ neighbor_of_root = node1
+ -- if node3 is closer to node2 than node1 is, use node3 as neighbor!
+ if node3 and dist_node_2_3 < dist_node_1_2 then neighbor_of_root = node3 end
+ else
+ root = node1
+ neighbor_of_root = node2
+ --check if node i-1 is closer to node1
+ local dist_node_0_1
+ if i>1 then
+ node0 = path[i-1]
+ dist_node_0_1 = self:edgeLength(node0, node1)
+ if dist_node_0_1 < dist_node_1_2 then neighbor_of_root = node0 end
+ end
+ end
+ break
+ end
+ end
+
+ return root, neighbor_of_root
+end
+
+
+return Maeusle2012
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/phylogenetics/PhylogeneticTree.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/phylogenetics/PhylogeneticTree.lua
new file mode 100644
index 0000000000..242ccf183f
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/phylogenetics/PhylogeneticTree.lua
@@ -0,0 +1,91 @@
+-- Copyright 2013 by Sarah Mäusle and 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$
+
+
+local PhylogeneticTree = {}
+
+
+-- Namespace
+require("pgf.gd.phylogenetics").PhylogeneticTree = PhylogeneticTree
+
+-- 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 = "phylogenetic tree layout",
+ algorithm = PhylogeneticTree,
+
+ postconditions = {
+ upward_oriented = true
+ },
+
+ summary = [["
+ Layout for drawing phylogenetic trees.
+ "]],
+ documentation = [["
+ ...
+ "]],
+ examples = [["
+ \tikz \graph [phylogenetic tree layout, upgma,
+ distance matrix={
+ 0 4 9 9 9 9 9
+ 4 0 9 9 9 9 9
+ 9 9 0 2 7 7 7
+ 9 9 2 0 7 7 7
+ 9 9 7 7 0 3 5
+ 9 9 7 7 3 0 5
+ 9 9 7 7 5 5 0}]
+ { a, b, c, d, e, f, g };
+ "]]
+}
+
+
+-- Computes a phylogenetic tree and/or visualizes it
+-- - computes a phylogenetic tree according to what the "phylogenetic
+-- algorithm" key is set to
+-- - invokes a graph drawing algorithm according to what the
+-- "phylogenetic layout" key is set to
+function PhylogeneticTree:run()
+
+ local options = self.digraph.options
+
+ -- Two storages for some information computed by the phylogenetic
+ -- tree generation algorithm
+ local lengths = Storage.newTableStorage()
+
+ -- First, compute the phylogenetic tree
+ local tree = options.algorithm_phases['phylogenetic tree generation'].new {
+ main_algorithm = self,
+ lengths = lengths
+ }:run()
+
+ tree = Direct.ugraphFromDigraph(tree)
+
+ -- Second, layout the tree
+ local layout_class = options.algorithm_phases['phylogenetic tree layout']
+ layout_class.new {
+ main_algorithm = self,
+ distances = distances,
+ lengths = lengths,
+ tree = tree
+ }:run()
+
+ tree:sync()
+end
+
+return PhylogeneticTree
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/phylogenetics/SokalMichener1958.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/phylogenetics/SokalMichener1958.lua
new file mode 100644
index 0000000000..6a665b6732
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/phylogenetics/SokalMichener1958.lua
@@ -0,0 +1,261 @@
+-- Copyright 2013 by Sarah Mäusle and 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$
+
+
+
+
+local SokalMichener1958 = {}
+
+
+-- Namespace
+require("pgf.gd.phylogenetics").SokalMichener1958 = SokalMichener1958
+
+-- Imports
+local InterfaceToAlgorithms = require("pgf.gd.interface.InterfaceToAlgorithms")
+local DistanceMatrix = require("pgf.gd.phylogenetics.DistanceMatrix")
+local lib = require("pgf.gd.lib")
+local Storage = require("pgf.gd.lib.Storage")
+local Digraph = require("pgf.gd.model.Digraph")
+
+-- Shorthand:
+local declare = InterfaceToAlgorithms.declare
+
+
+---
+declare {
+ key = "unweighted pair group method using arithmetic averages",
+ algorithm = SokalMichener1958,
+ phase = "phylogenetic tree generation",
+
+ summary = [["
+ The UPGMA (Unweighted Pair Group Method using arithmetic
+ Averages) algorithm of Sokal and Michener, 1958. It generates a
+ graph on the basis of such a distance matrix by generating nodes
+ and computing the edge lengths.
+ "]],
+ documentation = [["
+ This algorithm uses a distance matrix, ideally an ultrametric
+ one, to compute the graph.
+ "]],
+ examples = [["
+ \tikz \graph [phylogenetic tree layout, sibling distance=0pt, sibling sep=2pt,
+ unweighted pair group method using arithmetic averages,
+ distance matrix={
+ 0 4 9 9 9 9 9
+ 4 0 9 9 9 9 9
+ 9 9 0 2 7 7 7
+ 9 9 2 0 7 7 7
+ 9 9 7 7 0 3 5
+ 9 9 7 7 3 0 5
+ 9 9 7 7 5 5 0}]
+ { a, b, c, d, e, f, g };
+ "]]
+}
+
+
+---
+declare {
+ key = "upgma",
+ use = { { key = "unweighted pair group method using arithmetic averages" } },
+ summary = "An shorthand for |unweighted pair group method using arithmetic averages|"
+}
+
+
+
+
+--
+-- The run function of the upgma algorithm.
+--
+-- You must setup the following fields: The |main_algorithm| must
+-- store the main algorithm object (for phase |main|). The |distances|
+-- field must be a |Storage| object that will get filled with the
+-- distances computed by this algorithm. The |lengths| field must also
+-- be a |Storage| for the computed distances.
+--
+
+function SokalMichener1958:run()
+ self.distances = Storage.newTableStorage()
+
+ self.tree = Digraph.new(self.main_algorithm.digraph)
+
+ -- store the phylogenetic tree object, containing all user-specified
+ -- graph information
+ self:runUPGMA()
+ self:createFinalEdges()
+
+ return self.tree
+end
+
+
+
+-- UPGMA (Unweighted Pair Group Method using arithmetic Averages) algorithm
+-- (Sokal and Michener, 1958)
+--
+-- this function generates a graph on the basis of such a distance
+-- matrix by generating nodes and computing the edge lengths; the x-
+-- and y-positions of the nodes must be set separately
+--
+-- requirement: a distance matrix, ideally an ultrametric
+function SokalMichener1958:runUPGMA()
+ local matrix = DistanceMatrix.graphDistanceMatrix(self.tree)
+
+ local g = self.tree
+ local clusters = {}
+
+ -- create the clusters
+ for _,v in ipairs(g.vertices) do
+ clusters[#clusters+1] = self:newCluster(v)
+ end
+
+ -- Initialize the distances of these clusters:
+ for _,cx in ipairs(clusters) do
+ for _,cy in ipairs(clusters) do
+ cx.distances[cy] = matrix[cx.root][cy.root]
+ end
+ end
+
+ -- search for clusters with smallest distance and merge them
+ while #clusters > 1 do
+ local minimum_distance = math.huge
+ local min_cluster1
+ local min_cluster2
+ for i, cluster in ipairs (clusters) do
+ for j = i+1,#clusters do
+ local cluster2 = clusters[j]
+ local cluster_distance = self:getClusterDistance(cluster, cluster2)
+ if cluster_distance < minimum_distance then
+ minimum_distance, min_cluster1, min_cluster2 = cluster_distance, i, j
+ end
+ end
+ end
+ self:mergeClusters(clusters, min_cluster1, min_cluster2, minimum_distance)
+ end
+end
+
+
+-- a new cluster is created
+--
+-- @param vertex The vertex the cluster is initialized with
+--
+-- @return The new cluster
+function SokalMichener1958:newCluster(vertex)
+ return {
+ root = vertex, -- the root of the cluster
+ size = 1, -- the number of vertices in the cluster,
+ distances = {}, -- cached cluster distances to all other clusters
+ cluster_height = 0 -- this value is equivalent to half the distance of the last two clusters
+ -- that have been merged to form the current cluster;
+ -- necessary for determining the distances of newly generated nodes to their children.
+ }
+end
+
+
+-- gets the distance between two clusters
+--
+-- @param cluster1, cluster2 The two clusters
+--
+-- @return the distance between the clusters
+function SokalMichener1958:getClusterDistance(c,d)
+ return c.distances[d] or d.distances[c] or 0
+end
+
+
+-- merges two clusters by doing the following:
+-- - deletes cluster2 from the clusters table
+-- - adds all vertices from cluster2 to the vertices table of cluster1
+-- - updates the distances of the new cluster to all remaining clusters
+-- - generates a new node, as the new root of the cluster
+-- - computes the distance of the new node to the former roots (for
+-- later computation of the y-positions)
+-- - generates edges, connecting the new node to the former roots
+-- - updates the cluster height
+--
+-- @param clusters The array of clusters
+-- @param index_of_first_cluster The index of the first cluster
+-- @param index_of_second_cluster The index of the second cluster
+-- @param distance The distance between the two clusters
+
+function SokalMichener1958:mergeClusters(clusters, index_of_first_cluster, index_of_second_cluster, distance)
+
+ local g = self.tree
+ local cluster1 = clusters[index_of_first_cluster]
+ local cluster2 = clusters[index_of_second_cluster]
+
+ --update cluster distances
+ for i,cluster in ipairs (clusters) do
+ if cluster ~= cluster1 and cluster ~= cluster2 then
+ local dist1 = self:getClusterDistance (cluster1, cluster)
+ local dist2 = self:getClusterDistance (cluster2, cluster)
+ local dist = (dist1*cluster1.size + dist2*cluster2.size)/ (cluster1.size+cluster2.size)
+ cluster1.distances[cluster] = dist
+ cluster.distances[cluster1] = dist
+ end
+ end
+
+ -- delete cluster2
+ table.remove(clusters, index_of_second_cluster)
+
+ --add node and connect last vertex of each cluster with new node
+ local new_node = InterfaceToAlgorithms.createVertex(
+ self.main_algorithm,
+ {
+ name = "UPGMA-node ".. #self.tree.vertices+1,
+ generated_options = { { key = "phylogenetic inner node" } },
+ }
+ )
+ g:add{new_node}
+ -- the distance of the new node ( = the new root of the cluster) to its children (= the former roots) is
+ -- equivalent to half the distance between the two former clusters
+ -- minus the respective cluster height
+ local distance1 = distance/2-cluster1.cluster_height
+ self.distances[new_node][cluster1.root] = distance1
+ local distance2 = distance/2-cluster2.cluster_height
+ self.distances[new_node][cluster2.root] = distance2
+
+ -- these distances are also the final edge lengths, thus:
+ self.lengths[new_node][cluster1.root] = distance1
+ self.lengths[cluster1.root][new_node] = distance1
+
+ self.lengths[new_node][cluster2.root] = distance2
+ self.lengths[cluster2.root][new_node] = distance2
+
+ g:connect(new_node, cluster1.root)
+ g:connect(new_node, cluster2.root)
+
+ cluster1.root = new_node
+ cluster1.size = cluster1.size + cluster2.size
+ cluster1.cluster_height = distance/2 -- set new height of the cluster
+end
+
+
+
+-- generates edges for the final graph
+--
+-- throughout the process of creating the tree, arcs have been
+-- disconnected and connected, without truly creating edges. this is
+-- done in this function
+function SokalMichener1958:createFinalEdges()
+ local g = self.tree
+ local o_arcs = {} -- copy arcs since createEdge is going to modify the arcs array...
+ for _,arc in ipairs(g.arcs) do
+ o_arcs[#o_arcs+1] = arc
+ end
+ for _,arc in ipairs(o_arcs) do
+ InterfaceToAlgorithms.createEdge(
+ self.main_algorithm, arc.tail, arc.head,
+ { generated_options = {
+ { key = "phylogenetic edge", value = tostring(self.lengths[arc.tail][arc.head]) }
+ }})
+ end
+end
+
+
+return SokalMichener1958
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/phylogenetics/library.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/phylogenetics/library.lua
new file mode 100644
index 0000000000..5ab821c4a8
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/phylogenetics/library.lua
@@ -0,0 +1,35 @@
+-- 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$
+
+
+
+---
+-- A phylogenetic tree (or network) depicts the evolutionary history
+-- of species or, more generally, so called taxa. The present library
+-- includes a number of algorithms for drawing phylogenetic trees.
+--
+-- @library
+
+local phylogenetics -- Library name
+
+-- Main layout:
+require "pgf.gd.phylogenetics.PhylogeneticTree"
+
+-- Phylogenetic tree drawing:
+require "pgf.gd.phylogenetics.Maeusle2012"
+
+-- Phylogenetic tree generation:
+require "pgf.gd.phylogenetics.SokalMichener1958"
+require "pgf.gd.phylogenetics.BalancedMinimumEvolution"
+require "pgf.gd.phylogenetics.BalancedNearestNeighbourInterchange"
+require "pgf.gd.phylogenetics.AuthorDefinedPhylogeny"
+
+
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/planar.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/planar.lua
new file mode 100644
index 0000000000..5d80de77cd
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/planar.lua
@@ -0,0 +1,6 @@
+require "pgf"
+require "pgf.gd"
+
+pgf.gd.planar = {}
+
+return pgf.gd.planar
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/planar/BoyerMyrvold2004.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/planar/BoyerMyrvold2004.lua
new file mode 100644
index 0000000000..f587861d79
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/planar/BoyerMyrvold2004.lua
@@ -0,0 +1,678 @@
+
+--[[
+---Data structures---
+
+Vertices from the original ugraph are referred to as input vertices.
+The tables that contain vertex data relevant to the algorithm
+are referred to as vertices.
+A vertex table may have the following keys:
+
+-sign
+1 or -1, indicates whether this and all in the depth-first search
+following vertices must be considered flipped
+(i. e. adjacency lists reversed) in respect to the dfs parent
+
+-childlist
+A linked list containing all dfs children of the vertex whose virtual roots
+have not yet been merged into the vertex, sorted by lowpoint
+
+-adjlistlinks
+A table with two fields with keys 0 and 1, containing the two half edges
+of the vertex which lie on the external face of the graph
+(if the vertex lies on the external face).
+The half edge with key 0 lies in the 0-direction of the other half edge
+and vice-versa
+The two fields may hold the same half edge, if the vertex has degree one
+
+-pertinentroots
+A linked list containing all virtual roots of this vertex that are
+pertinent during the current step
+
+-inputvertex
+The input vertex that corresponds to the vertex
+
+-dfi
+The depth-first search index (number of the step in the dfs at which
+the vertex was discovered)
+
+-dfsparent
+The depth-first search parent (vertex from which the vertex was discovered
+first in the dfs)
+
+-leastancestor
+Dfi of the vertex with lowest dfi that can be reached using one back edge
+(non-tree edge)
+
+-lowpoint
+Dfi of the vertex with lowest dfi that can be reached using any number of
+tree edges plus one back edge
+
+
+A root vertex is a virtual vertex not contained in the original ugraph.
+The root vertex represents another vertex in a biconnected component (block)
+which is a child of the biconnected component the represented vertex is in.
+The only field that it has in common with other vertices is the
+adjacency list links array:
+
+-isroot
+always true, indicates that this vertex is a virtual root
+
+-rootparent
+The vertex which this root represents
+
+-rootchild
+The only dfs child of the original vertex which is contained in the
+root verticis biconnected component
+
+-adjlistlinks
+See adjlistlinks of a normal vertex
+
+
+A half edge is a table with the following fields:
+
+-links
+A table with two fields with keys 0 and 1, containing the neighboring
+half edges in the adjacency list of the vertex these edges originate from.
+
+-target
+The vertex the half edge leads to
+
+-twin
+The twin half edge which connects the two vertices in the opposite direction
+
+-shortcircuit
+True if the half edge was inserted in order to make a short circuit for the
+algorithm. The edge will be removed at the end.
+
+The BoyerMyrvold2004 class has the following fields:
+
+-inputgraph
+The original ugraph given to the algorithm
+
+-numvertices
+The number of vertices of the graph
+
+-vertices
+The vertex table with depth-first search indices as keys
+
+-verticesbyinputvertex
+The vertex table with input vertices as keys
+
+-verticesbylowpoint
+The vertex table with low points as keys
+
+-shortcircuitedges
+An array of all short circuit half edges
+(which may not be in the original graph and will be removed at the end)
+
+--]]
+
+local BM = {}
+require("pgf.gd.planar").BoyerMyrvold2004 = BM
+
+-- imports
+local Storage = require "pgf.gd.lib.Storage"
+local LinkedList = require "pgf.gd.planar.LinkedList"
+local Embedding = require "pgf.gd.planar.Embedding"
+
+-- create class properties
+BM.__index = BM
+
+function BM.new()
+ local t = {}
+ setmetatable(t, BM)
+ return t
+end
+
+-- initializes some data structures at the beginning
+-- takes the ugraph of the layout algorithm as input
+function BM:init(g)
+ self.inputgraph = g
+ self.numvertices = #g.vertices
+ self.vertices = {}
+ self.verticesbyinputvertex = Storage.new()
+ self.verticesbylowpoint = Storage.newTableStorage()
+ self.shortcircuitedges = {}
+ for _, inputvertex in ipairs(self.inputgraph.vertices) do
+ local vertex = {
+ sign = 1,
+ childlist = LinkedList.new(),
+ adjlistlinks = {},
+ pertinentroots = LinkedList.new(),
+ inputvertex = inputvertex,
+ }
+ setmetatable(vertex, Embedding.vertexmetatable)
+ self.verticesbyinputvertex[inputvertex] = vertex
+ end
+end
+
+--[[
+local function nilmax(a, b)
+ if a == nil then return b end
+ if b == nil then return a end
+ return math.max(a, b)
+end
+
+local function nilmin(a, b)
+ if a == nil then return b end
+ if b == nil then return a end
+ return math.min(a, b)
+end
+--]]
+
+-- the depth-first search of the preprocessing
+function BM:predfs(inputvertex, parent)
+ local dfi = #self.vertices + 1
+ local vertex = self.verticesbyinputvertex[inputvertex]
+ self.vertices[dfi] = vertex
+ -- set the dfs infos in the vertex
+ vertex.dfi = dfi
+ vertex.dfsparent = parent
+ vertex.leastancestor = dfi
+ vertex.lowpoint = dfi
+ -- find neighbors
+ for _, arc in ipairs(self.inputgraph:outgoing(inputvertex)) do
+ local ninputvertex = arc.head
+ assert(ninputvertex ~= inputvertex, "Self-loop detected!")
+ local nvertex = self.verticesbyinputvertex[ninputvertex]
+ if nvertex.dfi == nil then
+ -- new vertex discovered
+ self:predfs(ninputvertex, vertex) -- recursive call
+ vertex.lowpoint = math.min(vertex.lowpoint, nvertex.lowpoint)
+ elseif parent and ninputvertex ~= parent.inputvertex then
+ -- back edge found
+ vertex.leastancestor = math.min(vertex.leastancestor, nvertex.dfi)
+ vertex.lowpoint = math.min(vertex.lowpoint, nvertex.dfi)
+ end
+ end
+ -- put vertex into lowpoint sort bucket
+ table.insert(self.verticesbylowpoint[vertex.lowpoint], vertex)
+end
+
+-- the preprocessing at the beginning of the algorithm
+-- does the depth-first search and the bucket sort for the child lists
+function BM:preprocess()
+ -- make dfs starting at an arbitrary vertex
+ self:predfs(self.inputgraph.vertices[1])
+ -- create separated child lists with bucket sort
+ for i = 1, self.numvertices do
+ for _, vertex in ipairs(self.verticesbylowpoint[i]) do
+ if vertex.dfsparent then
+ vertex.childlistelement
+ = vertex.dfsparent.childlist:addback(vertex)
+ end
+ end
+ end
+end
+
+-- adds tree edges and the corresponding virtual root vertices
+-- of the currentvertex
+function BM:add_trivial_edges(vertex)
+ -- find all dfs children
+ for _, arc in ipairs(self.inputgraph:outgoing(vertex.inputvertex)) do
+ local nvertex = self.verticesbyinputvertex[arc.head]
+ if nvertex.dfsparent == vertex then
+ -- create root vertex
+ local rootvertex = {
+ isroot = true,
+ rootparent = vertex,
+ rootchild = nvertex,
+ adjlistlinks = {},
+ name = tostring(vertex) .. "^" .. tostring(nvertex)
+ }
+ setmetatable(rootvertex, Embedding.vertexmetatable)
+ nvertex.parentroot = rootvertex
+ -- create half edges
+ local halfedge1 = {target = nvertex, links = {}}
+ local halfedge2 = {target = rootvertex, links = {}}
+ halfedge1.twin = halfedge2
+ halfedge2.twin = halfedge1
+ -- create circular adjacency lists
+ halfedge1.links[0] = halfedge1
+ halfedge1.links[1] = halfedge1
+ halfedge2.links[0] = halfedge2
+ halfedge2.links[1] = halfedge2
+ -- create links to adjacency lists
+ rootvertex.adjlistlinks[0] = halfedge1
+ rootvertex.adjlistlinks[1] = halfedge1
+ nvertex.adjlistlinks[0] = halfedge2
+ nvertex.adjlistlinks[1] = halfedge2
+ end
+ end
+end
+
+-- for the external face vertex which was entered through link vin
+-- returns the successor on the external face and the link through
+-- which it was entered
+local function get_successor_on_external_face(vertex, vin)
+ local halfedge = vertex.adjlistlinks[1 - vin]
+ local svertex = halfedge.target
+ local sin
+ if vertex.adjlistlinks[0] == vertex.adjlistlinks[1] then
+ sin = vin
+ elseif svertex.adjlistlinks[0].twin == halfedge then
+ sin = 0
+ else
+ sin = 1
+ end
+ return svertex, sin
+end
+
+-- the "walkup", used to identify the pertinent subgraph,
+-- i. e. the subgraph that contains end points of backedges
+-- for one backedge this function will mark all virtual roots
+-- as pertinent that lie on the path between the backedge and the current vertex
+-- backvertex: a vertex that is an endpoint of a backedge to the current vertex
+-- currentvertex: the vertex of the current step
+-- returns a root vertex of the current step, if one was found
+local function walkup(backvertex, currentvertex)
+ local currentindex = currentvertex.dfi
+ -- set the backedgeflag
+ backvertex.backedgeindex = currentindex
+ -- initialize traversal variables for both directions
+ local x, xin, y, yin = backvertex, 1, backvertex, 0
+ while x ~= currentvertex do
+ if x.visited == currentindex or y.visited == currentindex then
+ -- we found a path that already has the pertinent roots marked
+ return nil
+ end
+ -- mark vertices as visited for later calls
+ x.visited = currentindex
+ y.visited = currentindex
+
+ -- check for rootvertex
+ local rootvertex
+ if x.isroot then
+ rootvertex = x
+ elseif y.isroot then
+ rootvertex = y
+ end
+ if rootvertex then
+ local rootchild = rootvertex.rootchild
+ local rootparent = rootvertex.rootparent
+ if rootvertex.rootparent == currentvertex then
+ -- we found the other end of the back edge
+ return rootvertex
+ elseif rootchild.lowpoint < currentindex then
+ -- the block we just traversed is externally active
+ rootvertex.pertinentrootselement
+ = rootparent.pertinentroots:addback(rootvertex)
+ else
+ -- the block we just traversed is internally active
+ rootvertex.pertinentrootselement
+ = rootparent.pertinentroots:addfront(rootvertex)
+ end
+ -- jump to parent block
+ x, xin, y, yin = rootvertex.rootparent, 1, rootvertex.rootparent, 0
+ else
+ -- just continue on the external face
+ x, xin = get_successor_on_external_face(x, xin)
+ y, yin = get_successor_on_external_face(y, yin)
+ end
+ end
+end
+
+-- inverts the adjacency of a vertex
+-- i. e. reverses the order of the adjacency list and flips the links
+local function invert_adjacency(vertex)
+ -- reverse the list
+ for halfedge in Embedding.adjacency_iterator(vertex.adjlistlinks[0]) do
+ halfedge.links[0], halfedge.links[1]
+ = halfedge.links[1], halfedge.links[0]
+ end
+ -- flip links
+ vertex.adjlistlinks[0], vertex.adjlistlinks[1]
+ = vertex.adjlistlinks[1], vertex.adjlistlinks[0]
+end
+
+-- merges two blocks by merging the virtual root of the child block
+-- into it's parent, while making sure the external face stays consistent
+-- by flipping the root block if needed
+-- mergeinfo contains four fields:
+-- root - the virtual root vertex
+-- parent - it's parent
+-- rout - the link of the root through which we have exited it
+-- during the walkdown
+-- pin - the link of the parent through which we have entered it
+-- during the walkdown
+local function mergeblocks(mergeinfo)
+ local root = mergeinfo.root
+ local parent = mergeinfo.parent
+ local rout = mergeinfo.rootout
+ local pin = mergeinfo.parentin
+ if pin == rout then
+ -- flip required
+ invert_adjacency(root)
+ root.rootchild.sign = -1
+ --rout = 1 - rout -- not needed
+ end
+
+ -- redirect edges of the root vertex
+ for halfedge in Embedding.adjacency_iterator(root.adjlistlinks[0]) do
+ halfedge.twin.target = parent
+ end
+
+ -- remove block from data structures
+ root.rootchild.parentroot = nil
+ parent.pertinentroots:remove(root.pertinentrootselement)
+ parent.childlist:remove(root.rootchild.childlistelement)
+
+ -- merge adjacency lists
+ parent.adjlistlinks[0].links[1] = root.adjlistlinks[1]
+ parent.adjlistlinks[1].links[0] = root.adjlistlinks[0]
+ root.adjlistlinks[0].links[1] = parent.adjlistlinks[1]
+ root.adjlistlinks[1].links[0] = parent.adjlistlinks[0]
+ parent.adjlistlinks[pin] = root.adjlistlinks[pin]
+end
+
+-- inserts a half edge pointing to "to" into the adjacency list of "from",
+-- replacing the link "linkindex"
+local function insert_half_edge(from, linkindex, to)
+ local halfedge = {target = to, links = {}}
+ halfedge.links[ linkindex] = from.adjlistlinks[ linkindex]
+ halfedge.links[1 - linkindex] = from.adjlistlinks[1 - linkindex]
+ from.adjlistlinks[ linkindex].links[1 - linkindex] = halfedge
+ from.adjlistlinks[1 - linkindex].links[ linkindex] = halfedge
+ from.adjlistlinks[linkindex] = halfedge
+ return halfedge
+end
+
+-- connect the vertices x and y through the links xout and yin
+-- if shortcircuit is true, the edge will be marked as a short circuit edge
+-- and removed at the end of the algorithm
+function BM:embed_edge(x, xout, y, yin, shortcircuit)
+ -- create half edges
+ local halfedgex = insert_half_edge(x, xout, y)
+ local halfedgey = insert_half_edge(y, yin, x)
+ halfedgex.twin = halfedgey
+ halfedgey.twin = halfedgex
+ -- short circuit handling
+ if shortcircuit then
+ halfedgex.shortcircuit = true
+ halfedgey.shortcircuit = true
+ table.insert(self.shortcircuitedges, halfedgex)
+ table.insert(self.shortcircuitedges, halfedgey)
+ end
+end
+
+-- returns true if the given vertex is pertinent at the current step
+local function pertinent(vertex, currentindex)
+ return vertex.backedgeindex == currentindex
+ or not vertex.pertinentroots:empty()
+end
+
+-- returns true if the given vertex is externally active at the current step
+local function externally_active(vertex, currentindex)
+ return vertex.leastancestor < currentindex
+ or (not vertex.childlist:empty()
+ and vertex.childlist:first().lowpoint < currentindex)
+end
+
+-- the "walkdown", which merges the pertinent subgraph and embeds
+-- back and short circuit edges
+-- childrootvertex - a root vertex of the current vertex
+-- which the walkdown will start at
+-- currentvertex - the vertex of the current step
+function BM:walkdown(childrootvertex, currentvertex)
+ local currentindex = currentvertex.dfi
+ local mergestack = {}
+ local numinsertededges = 0 -- to return the number for count check
+ -- two walkdowns into both directions
+ for vout = 0,1 do
+ -- initialize the traversal variables
+ local w, win = get_successor_on_external_face(childrootvertex, 1 - vout)
+ while w ~= childrootvertex do
+ if w.backedgeindex == currentindex then
+ -- we found a backedge endpoint
+ -- merge all pertinent roots we found
+ while #mergestack > 0 do
+ mergeblocks(table.remove(mergestack))
+ end
+ -- embed the back edge
+ self:embed_edge(childrootvertex, vout, w, win)
+ numinsertededges = numinsertededges + 1
+ w.backedgeindex = 0 -- this shouldn't be necessary
+ end
+ if not w.pertinentroots:empty() then
+ -- we found a pertinent vertex with child blocks
+ -- create merge info for the later merge
+ local mergeinfo = {}
+ mergeinfo.parent = w
+ mergeinfo.parentin = win
+ local rootvertex = w.pertinentroots:first()
+ mergeinfo.root = rootvertex
+ -- check both directions for active vertices
+ local x, xin = get_successor_on_external_face(rootvertex, 1)
+ local y, yin = get_successor_on_external_face(rootvertex, 0)
+ local xpertinent = pertinent(x, currentindex)
+ local xexternallyactive = externally_active(x, currentindex)
+ local ypertinent = pertinent(y, currentindex)
+ local yexternallyactive = externally_active(y, currentindex)
+ -- chose the direction with the best vertex
+ if xpertinent and not xexternallyactive then
+ w, win = x, xin
+ mergeinfo.rootout = 0
+ elseif ypertinent and not yexternallyactive then
+ w, win = y, yin
+ mergeinfo.rootout = 1
+ elseif xpertinent then
+ w, win = x, xin
+ mergeinfo.rootout = 0
+ else
+ w, win = y, yin
+ mergeinfo.rootout = 1
+ end
+ -- this is what the paper says, but it might cause problems
+ -- not sure though...
+ --[[if w == x then
+ mergeinfo.rootout = 0
+ else
+ mergeinfo.rootout = 1
+ end--]]
+ table.insert(mergestack, mergeinfo)
+ elseif not pertinent(w, currentindex)
+ and not externally_active(w, currentindex) then
+ -- nothing to see here, just continue on the external face
+ w, win = get_successor_on_external_face(w, win)
+ else
+ -- this is a stopping vertex, walkdown will end here
+ -- paper puts this into the if,
+ -- but this should always be the case, i think
+ assert(childrootvertex.rootchild.lowpoint < currentindex)
+ if #mergestack == 0 then
+ -- we're in the block we started at, so we embed a back edge
+ self:embed_edge(childrootvertex, vout, w, win, true)
+ end
+ break
+ end
+ end
+ if #mergestack > 0 then
+ -- this means, there is a pertinent vertex blocked by stop vertices,
+ -- so the graph is not planar and we can skip the second walkdown
+ break
+ end
+ end
+ return numinsertededges
+end
+
+-- embeds the back edges for the current vertex
+-- walkup and walkdown are called from here
+-- returns true, if all back edges could be embedded
+function BM:add_back_edges(vertex)
+ local pertinentroots = {} -- not in the paper
+ local numbackedges = 0
+ -- find all back edges to vertices with lower dfi
+ for _, arc in ipairs(self.inputgraph:outgoing(vertex.inputvertex)) do
+ local nvertex = self.verticesbyinputvertex[arc.head]
+ if nvertex.dfi > vertex.dfi
+ and nvertex.dfsparent ~= vertex
+ and nvertex ~= vertex.dfsparent then
+ numbackedges = numbackedges + 1
+ -- do the walkup
+ local rootvertex = walkup(nvertex, vertex)
+ if rootvertex then
+ -- remember the root vertex the walkup found, so we don't
+ -- have to call the walkdown for all root vertices
+ -- (or even know what the root vertices are)
+ table.insert(pertinentroots, rootvertex)
+ end
+ end
+ end
+ -- for all root vertices the walkup found
+ local insertededges = 0
+ while #pertinentroots > 0 do
+ -- do the walkdown
+ insertededges = insertededges
+ + self:walkdown(table.remove(pertinentroots), vertex)
+ end
+ if insertededges ~= numbackedges then
+ -- not all back edges could be embedded -> graph is not planar
+ return false
+ end
+ return true
+end
+
+-- the depth-first search of the postprocessing
+-- flips the blocks according to the sign field
+function BM:postdfs(vertex, sign)
+ sign = sign or 1
+ local root = vertex.parentroot
+ if root then
+ sign = 1
+ else
+ sign = sign * vertex.sign
+ end
+
+ if sign == -1 then
+ -- number of flips is odd, so we need to flip here
+ invert_adjacency(vertex)
+ end
+
+ -- for all dfs children
+ for _, arc in ipairs(self.inputgraph:outgoing(vertex.inputvertex)) do
+ local nvertex = self.verticesbyinputvertex[arc.head]
+ if nvertex.dfsparent == vertex then
+ -- recursive call
+ self:postdfs(nvertex, sign)
+ end
+ end
+end
+
+-- the postprocessing at the end of the algorithm
+-- calls the post depth-first search,
+-- removes the short circuit edges from the adjacency lists,
+-- adjusts the links of the vertices,
+-- merges root vertices
+-- and cleans up the vertices
+function BM:postprocess()
+ -- flip components
+ self:postdfs(self.vertices[1])
+
+ -- unlink the short circuit edges
+ for _, halfedge in ipairs(self.shortcircuitedges) do
+ halfedge.links[0].links[1] = halfedge.links[1]
+ halfedge.links[1].links[0] = halfedge.links[0]
+ end
+
+ -- vertex loop
+ local rootvertices = {}
+ local edgetoface = {}
+ for _, vertex in ipairs(self.vertices) do
+ -- check for root vertex and save it
+ local root = vertex.parentroot
+ if root then
+ table.insert(rootvertices, root)
+ end
+
+ -- clean up links and create adjacency matrix
+ local link = vertex.adjlistlinks[0]
+ local adjmat = {}
+ vertex.adjmat = adjmat
+ if link then
+ -- make sure the link points to a half edge
+ -- that is no short circuit edge
+ while link.shortcircuit do
+ link = link.links[0]
+ end
+ -- create link
+ vertex.link = link
+
+ -- create adjacency matrix
+ for halfedge in Embedding.adjacency_iterator(link) do
+ setmetatable(halfedge, Embedding.halfedgemetatable)
+ local target = halfedge.target
+ if target.isroot then
+ target = target.rootparent
+ end
+ adjmat[target] = halfedge
+ end
+ end
+
+ -- clean up vertex
+ vertex.sign = nil
+ vertex.childlist = nil
+ vertex.adjlistlinks = nil
+ vertex.pertinentroots = nil
+ vertex.dfi = nil
+ vertex.dfsparent = nil
+ vertex.leastancestor = nil
+ vertex.lowpoint = nil
+ vertex.parentroot = nil
+ end
+
+ -- root vertex loop
+ for _, root in ipairs(rootvertices) do
+ -- make sure the links point to a half edges
+ -- that are no short circuit edge
+ local link = root.adjlistlinks[0]
+ while link.shortcircuit do
+ link = link.links[0]
+ end
+
+ -- merge into parent
+ local rootparent = root.rootparent
+ local parentlink = rootparent.link
+ local adjmat = rootparent.adjmat
+ for halfedge in Embedding.adjacency_iterator(link) do
+ setmetatable(halfedge, Embedding.halfedgemetatable)
+ halfedge.twin.target = rootparent
+ adjmat[halfedge.target] = halfedge
+ end
+ if parentlink == nil then
+ assert(rootparent.link == nil)
+ rootparent.link = link
+ else
+ -- merge adjacency lists
+ parentlink.links[0].links[1] = link
+ link.links[0].links[1] = parentlink
+ local tmp = link.links[0]
+ link.links[0] = parentlink.links[0]
+ parentlink.links[0] = tmp
+ end
+ end
+end
+
+-- the entry point of the algorithm
+-- returns the array of vertices
+-- the vertices now only contain the inputvertex field
+-- and a field named "link" which contains an arbitrary half edge
+-- from the respective adjacency list
+-- the adjacency lists are in a circular order in respect to the plane graph
+function BM:run()
+ self:preprocess()
+ -- main loop over all vertices from lowest dfi to highest
+ for i = self.numvertices, 1, -1 do
+ local vertex = self.vertices[i]
+ self:add_trivial_edges(vertex)
+ if not self:add_back_edges(vertex) then
+ -- graph not planar
+ return nil
+ end
+ end
+ self:postprocess()
+ local embedding = Embedding.new()
+ embedding.vertices = self.vertices
+ return embedding
+end
+
+return BM
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/planar/Embedding.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/planar/Embedding.lua
new file mode 100644
index 0000000000..8d9d3b53b1
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/planar/Embedding.lua
@@ -0,0 +1,788 @@
+local E = {}
+
+require("pgf.gd.planar").Embedding = E
+
+-- includes
+local LinkedList = require("pgf.gd.planar.LinkedList")
+
+E.vertexmetatable = {
+ __tostring = function(v)
+ if v.name then
+ return v.name
+ elseif v.inputvertex then
+ return v.inputvertex.name
+ else
+ return tostring(v)
+ end
+ end
+}
+
+E.halfedgemetatable = {
+ __tostring = function(e)
+ return tostring(e.twin.target)
+ .. " -> "
+ .. tostring(e.target)
+ end
+}
+
+-- create class properties
+E.__index = E
+
+function E.new()
+ local t = {
+ vertices = {},
+ }
+ setmetatable(t, E)
+ return t
+end
+
+function E:add_vertex(name, inputvertex, virtual)
+ virtual = virtual or nil
+ local vertex = {
+ adjmat = {},
+ name = name,
+ inputvertex = inputvertex,
+ virtual = virtual,
+ }
+ setmetatable(vertex, E.vertexmetatable)
+ table.insert(self.vertices, vertex)
+ return vertex
+end
+
+function E:add_edge(v1, v2, after1, after2, virtual)
+ assert(v1.link == nil or v1 == after1.twin.target)
+ assert(v2.link == nil or v2 == after2.twin.target)
+ assert(v1.adjmat[v2] == nil)
+ assert(v2.adjmat[v1] == nil)
+
+ virtual = virtual or nil
+
+ local halfedge1 = {
+ target = v2,
+ virtual = virtual,
+ links = {},
+ }
+ local halfedge2 = {
+ target = v1,
+ virtual = virtual,
+ links = {},
+ }
+ halfedge1.twin = halfedge2
+ halfedge2.twin = halfedge1
+
+ setmetatable(halfedge1, E.halfedgemetatable)
+ setmetatable(halfedge2, E.halfedgemetatable)
+
+ if v1.link == nil then
+ v1.link = halfedge1
+ halfedge1.links[0] = halfedge1
+ halfedge1.links[1] = halfedge1
+ else
+ halfedge1.links[0] = after1.links[0]
+ after1.links[0].links[1] = halfedge1
+ halfedge1.links[1] = after1
+ after1.links[0] = halfedge1
+ end
+
+ if v2.link == nil then
+ v2.link = halfedge2
+ halfedge2.links[0] = halfedge2
+ halfedge2.links[1] = halfedge2
+ else
+ halfedge2.links[0] = after2.links[0]
+ after2.links[0].links[1] = halfedge2
+ halfedge2.links[1] = after2
+ after2.links[0] = halfedge2
+ end
+
+ v1.adjmat[v2] = halfedge1
+ v2.adjmat[v1] = halfedge2
+
+ return halfedge1, halfedge2
+end
+
+function E:remove_virtual()
+ local virtuals = {}
+ for i, v in ipairs(self.vertices) do
+ if v.virtual then
+ table.insert(virtuals, i)
+ else
+ local start = v.link
+ local current = start
+ repeat
+ current = current.links[0]
+ if current.virtual then
+ current.links[0].links[1] = current.links[1]
+ current.links[1].links[0] = current.links[0]
+ v.adjmat[current.target] = nil
+ current.target.adjmat[v] = nil
+ end
+ until current == start
+ end
+ end
+ for i = #virtuals, 1, -1 do
+ self.vertices[virtuals[i]] = self.vertices[#self.vertices]
+ table.remove(self.vertices)
+ end
+end
+
+-- for the use in for-loops
+-- iterates over the adjacency list of a vertex
+-- given a half edge to start and a direction (0 or 1, default 0)
+function E.adjacency_iterator(halfedge, direction)
+ direction = direction or 0
+ local function next_edge(startedge, prevedge)
+ if prevedge == nil then
+ return startedge
+ else
+ local nextedge = prevedge.links[direction]
+ if nextedge ~= startedge then
+ return nextedge
+ else
+ return nil
+ end
+ end
+ end
+ return next_edge, halfedge, nil
+end
+
+function E.face_iterator(halfedge, direction)
+ direction = direction or 0
+ local function next_edge(startedge, prevedge)
+ if prevedge == nil then
+ return startedge
+ else
+ local nextedge = prevedge.twin.links[1 - direction]
+ if nextedge ~= startedge then
+ return nextedge
+ else
+ return nil
+ end
+ end
+ end
+ return next_edge, halfedge, nil
+end
+
+function E:triangulate()
+ local visited = {}
+ for _, vertex in ipairs(self.vertices) do
+ for start in E.adjacency_iterator(vertex.link) do
+ if not visited[start] then
+ local prev = start
+ local beforestart = start.links[0].twin
+ local current = start.twin.links[1]
+ local next = current.twin.links[1]
+ visited[start] = true
+ visited[current] = true
+ visited[next] = true
+ while next ~= beforestart do
+ local halfedge1, halfedge2
+ if vertex ~= current.target
+ and not vertex.adjmat[current.target] then
+ halfedge1, halfedge2 = self:add_edge(
+ vertex, current.target,
+ prev, next,
+ true
+ )
+
+ prev = halfedge1
+ current = next
+ next = next.twin.links[1]
+ elseif not prev.target.adjmat[next.target] then
+ halfedge1, halfedge2 = self:add_edge(
+ prev.target, next.target,
+ current, next.twin.links[1],
+ true
+ )
+
+ current = halfedge1
+ next = halfedge2.links[1]
+ else
+ local helper = next.twin.links[1]
+ halfedge1, halfedge2 = self:add_edge(
+ current.target, helper.target,
+ next, helper.twin.links[1],
+ true
+ )
+
+ next = halfedge1
+ end
+
+ visited[next] = true
+ visited[halfedge1] = true
+ visited[halfedge2] = true
+ end
+ end
+ end
+ end
+end
+
+function E:canonical_order(v1, v2, vn)
+ local n = #self.vertices
+ local order = { v1 }
+ local marks = { [v1] = "ordered", [v2] = 0 }
+ local visited = {}
+ local vk = v1
+ local candidates = LinkedList.new()
+ local listelements = {}
+ for k = 1, n-2 do
+ for halfedge in E.adjacency_iterator(vk.link) do
+ local vertex = halfedge.target
+ if vertex ~= vn then
+ local twin = halfedge.twin
+ visited[twin] = true
+ if marks[vertex] == nil then
+ marks[vertex] = "visited"
+ elseif marks[vertex] ~= "ordered" then
+ local neighbor1 = visited[twin.links[0]]
+ local neighbor2 = visited[twin.links[1]]
+ if marks[vertex] == "visited" then
+ if neighbor1 or neighbor2 then
+ marks[vertex] = 1
+ listelements[vertex] = candidates:addback(vertex)
+ else
+ marks[vertex] = 2
+ end
+ else
+ if neighbor1 == neighbor2 then
+ if neighbor1 and neighbor2 then
+ marks[vertex] = marks[vertex] - 1
+ else
+ marks[vertex] = marks[vertex] + 1
+ end
+ if marks[vertex] == 1 then
+ listelements[vertex]
+ = candidates:addback(vertex)
+ elseif listelements[vertex] then
+ candidates:remove(listelements[vertex])
+ listelements[vertex] = nil
+ end
+ end
+ end
+ end
+ end
+ end
+ vk = candidates:popfirst()
+ order[k+1] = vk
+ marks[vk] = "ordered"
+ end
+ order[n] = vn
+ return order
+end
+
+function E:get_biggest_face()
+ local number = 0
+ local edge
+ local visited = {}
+ for _, vertex in ipairs(self.vertices) do
+ for start in E.adjacency_iterator(vertex.link) do
+ local count = 0
+ if not visited[start] then
+ visited[start] = true
+ local current = start
+ repeat
+ count = count + 1
+ current = current.twin.links[1]
+ until current == start
+ if count > number then
+ number = count
+ edge = start
+ end
+ end
+ end
+ end
+ return edge, number
+end
+
+function E:surround_by_triangle(faceedge, facesize)
+ local divisor = 3
+ if facesize > 3 then
+ divisor = 4
+ end
+ local basenodes = math.floor(facesize / divisor)
+ local extranodes = facesize % divisor
+ local attachnodes = { basenodes, basenodes, basenodes }
+ if facesize > 3 then
+ attachnodes[2] = basenodes * 2
+ end
+ for i = 1,extranodes do
+ attachnodes[i] = attachnodes[i] + 1
+ end
+
+ local v = {
+ self:add_vertex("$v_1$", nil, true),
+ self:add_vertex("$v_n$", nil, true),
+ self:add_vertex("$v_2$", nil, true)
+ }
+ for i = 1,3 do
+ local currentv = v[i]
+ local nextv = v[i % 3 + 1]
+ self:add_edge(currentv, nextv, currentv.link, nextv.link, true)
+ end
+
+ local current = faceedge
+ local next = current.twin.links[1]
+ for i = 1,3 do
+ local vertex = v[i]
+ local otheredge = vertex.adjmat[v[i % 3 + 1]]
+ local previnserted = otheredge.links[1]
+ for count = 1, attachnodes[i] do
+ if not vertex.adjmat[current.target] then
+ previnserted, _ = self:add_edge(
+ vertex, current.target,
+ previnserted, next,
+ true
+ )
+ end
+
+ current = next
+ next = next.twin.links[1]
+ end
+ if not vertex.adjmat[current.target] then
+ previnserted, _ = self:add_edge(
+ vertex, current.target,
+ previnserted, next,
+ true
+ )
+ current = previnserted
+ end
+ end
+ return v[1], v[3], v[2]
+end
+
+function E:improve()
+ local pairdata = {}
+ local inpair = {}
+ for i, v1 in ipairs(self.vertices) do
+ for j = i + 1, #self.vertices do
+ local v2 = self.vertices[j]
+ local pd = self:find_pair_components(v1, v2)
+ if pd then
+ inpair[v1] = true
+ inpair[v2] = true
+ table.insert(pairdata, pd)
+ end
+ end
+ if not inpair[v1] then
+ local pd = self:find_pair_components(v1, nil)
+ if pd then
+ inpair[v1] = true
+ table.insert(pairdata, pd)
+ end
+ end
+ end
+
+ local changed
+ local runs = 1
+ local edgepositions = {}
+ repeat
+ changed = false
+ for i, pd in ipairs(pairdata) do
+ self:improve_separation_pair(pd)
+ end
+ -- check for changes
+ for i, v in ipairs(self.vertices) do
+ local start = v.link
+ local current = start
+ local counter = 1
+ repeat
+ if counter ~= edgepositions[current] then
+ changed = true
+ edgepositions[current] = counter
+ end
+ counter = counter + 1
+ current = current.links[0]
+ until current == start
+ end
+ runs = runs + 1
+ until changed == false or runs > 100
+end
+
+function E:find_pair_components(v1, v2)
+ local visited = {}
+ local companchors = {}
+ local edgecomps = {}
+ local compvertices = {}
+ local islinear = {}
+ local edgeindices = {}
+
+ local pair = { v1, v2 }
+ local start = v1.link
+ local current = start
+ local edgeindex = 1
+ -- start searches from v1
+ repeat
+ edgeindices[current] = edgeindex
+ edgeindex = edgeindex + 1
+ if not edgecomps[current] then
+ local compindex = #companchors + 1
+ local ca, il
+ edgecomps[current] = compindex
+ compvertices[compindex] = {}
+ local target = current.target
+ if target == v2 then
+ edgecomps[current.twin] = compindex
+ ca = 3
+ il = true
+ else
+ ca, il = self:component_dfs(
+ target,
+ pair,
+ visited,
+ edgecomps,
+ compvertices[compindex],
+ compindex
+ )
+ end
+ companchors[compindex] = ca
+ islinear[compindex] = il
+ end
+ current = current.links[0]
+ until current == start
+
+ if v2 then
+ start = v2.link
+ current = start
+ local lastincomp = true
+ local edgeindex = 1
+ -- now find the remaining blocks at v2
+ repeat
+ edgeindices[current] = edgeindex
+ edgeindex = edgeindex + 1
+ if not edgecomps[current] then
+ local compindex = #companchors + 1
+ edgecomps[current] = compindex
+ compvertices[compindex] = {}
+ self:component_dfs(
+ current.target,
+ pair,
+ visited,
+ edgecomps,
+ compvertices[compindex],
+ compindex
+ )
+ companchors[compindex] = 2
+ end
+ current = current.links[0]
+ until current == start
+ end
+
+ -- init compedges, tricomps, twocomps
+ local tricomps = {}
+ local twocomps = {{}, {}}
+ for i, anchors in ipairs(companchors) do
+ if anchors == 3 then
+ table.insert(tricomps, i)
+ else
+ table.insert(twocomps[anchors], i)
+ end
+ end
+
+ local flipimmune = #tricomps == 2
+ and (islinear[tricomps[1]] or islinear[tricomps[2]])
+ if (#tricomps < 2 or flipimmune)
+ and (v2 ~= nil or #twocomps[1] < 2) then
+ return nil
+ end
+
+ -- order tri comps cyclic
+ local function sorter(a, b)
+ return #compvertices[a] < #compvertices[b]
+ end
+
+ table.sort(tricomps, sorter)
+
+ -- determine order of comps
+ local numtricomps = #tricomps
+ local comporder = { {}, {} }
+ local bottom = math.ceil(numtricomps / 2)
+ local top = bottom + 1
+ for i, comp in ipairs(tricomps) do
+ if i % 2 == 1 then
+ comporder[1][bottom] = comp
+ comporder[2][numtricomps - bottom + 1] = comp
+ bottom = bottom - 1
+ else
+ comporder[1][top] = comp
+ comporder[2][numtricomps - top + 1] = comp
+ top = top + 1
+ end
+ end
+
+ local pairdata = {
+ pair = pair,
+ companchors = companchors,
+ edgecomps = edgecomps,
+ edgeindices = edgeindices,
+ compvertices = compvertices,
+ tricomps = tricomps,
+ twocomps = twocomps,
+ comporder = comporder,
+ }
+ return pairdata
+end
+
+function E:component_dfs(v, pair, visited, edgecomps, compvertices, compindex)
+ visited[v] = true
+ local start = v.link
+ local current = start
+ local companchors = 1
+ local numedges = 0
+ local islinear = true
+ table.insert(compvertices, v)
+ repeat
+ numedges = numedges + 1
+ local target = current.target
+ if target == pair[1] or target == pair[2] then
+ edgecomps[current.twin] = compindex
+ if target == pair[2] then
+ companchors = 3
+ end
+ elseif not visited[target] then
+ local ca, il = self:component_dfs(
+ target,
+ pair,
+ visited,
+ edgecomps,
+ compvertices,
+ compindex
+ )
+ if ca == 3 then
+ companchors = 3
+ end
+ islinear = islinear and il
+ end
+ current = current.links[0]
+ until current == start
+ return companchors, islinear and numedges == 2
+end
+
+function E:improve_separation_pair(pairdata)
+ local pair = pairdata.pair
+ local companchors = pairdata.companchors
+ local edgecomps = pairdata.edgecomps
+ local edgeindices = pairdata.edgeindices
+ local compvertices = pairdata.compvertices
+ local tricomps = pairdata.tricomps
+ local twocomps = pairdata.twocomps
+ local comporder = pairdata.comporder
+ local v1 = pair[1]
+ local v2 = pair[2]
+
+ local compedges = {}
+ for i = 1, #companchors do
+ compedges[i] = {{}, {}}
+ end
+
+ local numtricomps = #tricomps
+ local numtwocomps = { #twocomps[1], #twocomps[2] }
+
+ -- find compedges
+ for i = 1, #pair do
+ -- first find an edge that is the first of a triconnected component
+ local start2
+ if v2 then
+ start = pair[i].link
+ current = start
+ local last
+ repeat
+ local comp = edgecomps[current]
+ if companchors[comp] == 3 then
+ if last == nil then
+ last = comp
+ elseif last ~= comp then
+ start2 = current
+ break
+ end
+ end
+ current = current.links[0]
+ until current == start
+ else
+ start2 = pair[i].link
+ end
+ -- now list the edges by components
+ current = start2
+ repeat
+ table.insert(compedges[edgecomps[current]][i], current)
+ current = current.links[0]
+ until current == start2
+ end
+
+ -- count edges on each side of tri comps
+ local edgecount = {}
+ for _, comp in ipairs(tricomps) do
+ edgecount[comp] = {}
+ for i = 1, #pair do
+ local count = 1
+ local current = compedges[comp][i][1]
+ local other = pair[3 - i]
+ while current.target ~= other do
+ count = count + 1
+ current = current.twin.links[0]
+ end
+ edgecount[comp][i] = count
+ end
+ end
+
+ -- determine which comps have to be flipped
+ local flips = {}
+ local numflips = 0
+ local allflipped = true
+ for i, comp in ipairs(comporder[1]) do
+ local side1, side2
+ if i > numtricomps / 2 then
+ side1 = edgecount[comp][1]
+ side2 = edgecount[comp][2]
+ else
+ side1 = edgecount[comp][2]
+ side2 = edgecount[comp][1]
+ end
+ if side1 > side2 then
+ numflips = numflips + 1
+ flips[comp] = true
+ elseif side1 < side2 then
+ allflipped = false
+ end
+ end
+
+ if allflipped then
+ for i, comp in ipairs(tricomps) do
+ flips[comp] = false
+ end
+ else
+ for i, comp in ipairs(tricomps) do
+ if flips[comp] then
+ for _, v in ipairs(compvertices[comp]) do
+ local start = v.link
+ local current = start
+ repeat
+ current.links[0], current.links[1]
+ = current.links[1], current.links[0]
+ current = current.links[1]
+ until current == start
+ end
+ end
+ end
+ end
+
+ -- order edges cyclic per component (one cycle for all tri comps)
+ for i = 1, #pair do
+ if v2 then
+ local co
+ if allflipped then
+ co = comporder[3 - i]
+ else
+ co = comporder[i]
+ end
+
+ local id = co[numtricomps]
+ lastedges = compedges[id][i]
+ if flips[id] then
+ lastedge = lastedges[1]
+ else
+ lastedge = lastedges[#lastedges]
+ end
+
+ -- tri comps
+ for _, id in ipairs(co) do
+ local edges = compedges[id][i]
+ local from
+ local to
+ local step
+ if flips[id] then
+ from = #edges
+ to = 1
+ step = -1
+ else
+ from = 1
+ to = #edges
+ step = 1
+ end
+ for k = from, to, step do
+ local edge = edges[k]
+ lastedge.links[0] = edge
+ edge.links[1] = lastedge
+ lastedge = edge
+ end
+ end
+ end
+
+ -- two comps
+ for _, id in ipairs(twocomps[i]) do
+ lastedges = compedges[id][i]
+ lastedge = lastedges[#lastedges]
+ for _, edge in ipairs(compedges[id][i]) do
+ lastedge.links[0] = edge
+ edge.links[1] = lastedge
+ lastedge = edge
+ end
+ end
+ end
+
+ -- now merge the cycles
+ for i = 1, #pair do
+ local outeredges = {}
+ -- find the biggest face of the tri comps
+ if v2 then
+ local biggestedge
+ local biggestsize
+ local biggestindex
+ local start = compedges[tricomps[1]][i][1]
+ local current = start
+ repeat
+ local size = self:get_face_size(current)
+ if not biggestedge or size > biggestsize
+ or (size == biggestsize
+ and edgeindices[current] > biggestindex) then
+ biggestedge = current
+ biggestsize = size
+ biggestindex = edgeindices[current]
+ end
+ current = current.links[0]
+ until current == start
+ outeredges[1] = biggestedge
+ end
+
+ -- now for every two comp
+ for _, id in ipairs(twocomps[i]) do
+ local biggestedge
+ local biggestsize
+ local biggestindex
+ local start = compedges[id][i][1]
+ local current = start
+ repeat
+ local size = self:get_face_size(current)
+ if not biggestedge or size > biggestsize
+ or (size == biggestsize
+ and edgeindices[current] > biggestindex) then
+ biggestedge = current
+ biggestsize = size
+ biggestindex = edgeindices[current]
+ end
+ current = current.links[0]
+ until current == start
+ table.insert(outeredges, biggestedge)
+ end
+
+ -- now merge all comps at the outer edges
+ local lastedge = outeredges[#outeredges].links[0]
+ for _, edge in ipairs(outeredges) do
+ local nextlastedge = edge.links[0]
+ lastedge.links[1] = edge
+ edge.links[0] = lastedge
+ lastedge = nextlastedge
+ end
+ end
+end
+
+function E:get_face_size(halfedge)
+ local size = 0
+ local current = halfedge
+ repeat
+ size = size + 1
+ current = current.twin.links[1]
+ until current == halfedge
+ return size
+end
+
+return E
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/planar/LinkedList.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/planar/LinkedList.lua
new file mode 100644
index 0000000000..35f8418fdf
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/planar/LinkedList.lua
@@ -0,0 +1,88 @@
+local LinkedList = {}
+
+LinkedList.__index = LinkedList
+
+function LinkedList.new()
+ local list = {elements = {}}
+ setmetatable(list, LinkedList)
+ return list
+end
+
+function LinkedList:addback(payload)
+ if payload == nil then
+ error("Need a payload!", 2)
+ end
+ local element = { payload = payload }
+ if self.head then
+ local tail = self.head.prev
+ self.head.prev = element
+ tail.next = element
+ element.next = self.head
+ element.prev = tail
+ else
+ self.head = element
+ element.next = element
+ element.prev = element
+ end
+ self.elements[element] = true
+ return element
+end
+
+function LinkedList:addfront(payload)
+ self.head = self:addback(payload)
+ return self.head
+end
+
+function LinkedList:remove(element)
+ if self.elements[element] == nil then
+ error("Element not in list!", 2)
+ end
+ if self.head == element then
+ if element.next == element then
+ self.head = nil
+ else
+ self.head = element.next
+ end
+ end
+ element.prev.next = element.next
+ element.next.prev = element.prev
+ self.elements[element] = nil
+end
+
+function LinkedList:popfirst()
+ if self.head == nil then
+ return nil
+ end
+ local element = self.head
+ if element.next == element then
+ self.head = nil
+ else
+ self.head = element.next
+ element.next.prev = element.prev
+ element.prev.next = element.next
+ end
+ self.elements[element] = nil
+ return element.payload
+end
+
+function LinkedList:poplast()
+ if self.head == nil then
+ return nil
+ end
+ self.head = self.head.prev
+ return self:popfirst()
+end
+
+function LinkedList:first()
+ return self.head and self.head.payload
+end
+
+function LinkedList:last()
+ return self.head and self.head.prev.payload
+end
+
+function LinkedList:empty()
+ return self.head == nil
+end
+
+return LinkedList
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/planar/List.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/planar/List.lua
new file mode 100644
index 0000000000..22a91e6571
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/planar/List.lua
@@ -0,0 +1,49 @@
+local List = {}
+
+List.__index = List
+
+function List.new()
+ local t = {first = 0, last = -1}
+ setmetatable(t, List)
+ return t
+end
+
+function List:pushleft(value)
+ local first = self.first - 1
+ self.first = first
+ self[first] = value
+end
+
+function List:pushright(value)
+ local last = self.last + 1
+ self.last = last
+ self[last] = value
+end
+
+function List:popleft()
+ local first = self.first
+ if first > self.last then error("List is empty") end
+ local value = self[first]
+ self[first] = nil
+ self.first = first + 1
+ return value
+end
+
+function List:popright()
+ local last = self.last
+ if self.first > last then error("List is empty") end
+ local value = self[last]
+ self[last] = nil
+ self.last = last - 1
+ return value
+end
+
+function List:size()
+ return self.last - self.first + 1
+end
+
+function List:empty()
+ return self.last < self.first
+end
+
+return List
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/planar/PDP.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/planar/PDP.lua
new file mode 100644
index 0000000000..8c159cf61d
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/planar/PDP.lua
@@ -0,0 +1,576 @@
+
+local PDP = {}
+require("pgf.gd.planar").PDP = PDP
+
+-- Imports
+local declare = require("pgf.gd.interface.InterfaceToAlgorithms").declare
+local Storage = require "pgf.gd.lib.Storage"
+local Coordinate = require "pgf.gd.model.Coordinate"
+local Path = require "pgf.gd.model.Path"
+---
+PDP.__index = PDP
+
+function PDP.new(ugraph, embedding,
+ delta, gamma, coolingfactor,
+ expiterations,
+ startrepexp, endrepexp,
+ startattexp, endattexp,
+ appthreshold, stretchthreshold,
+ stresscounterthreshold,
+ numdivisions)
+ local t = {
+ ugraph = ugraph,
+ embedding = embedding,
+ delta = delta ,
+ gamma = gamma,
+ coolingfactor = coolingfactor,
+ expiterations = expiterations,
+ startrepexp = startrepexp,
+ endrepexp = endrepexp,
+ startattexp = startattexp,
+ endattexp = endattexp,
+ appthreshold = appthreshold,
+ stretchthreshold = stretchthreshold,
+ stresscounterthreshold = stresscounterthreshold,
+ numdivisions = numdivisions,
+ posxs = {},
+ posys = {},
+ cvsxs = {},
+ cvsys = {},
+ embeddingedges = {},
+ edgeids = {},
+ numedgeids = 0,
+ vertexids = {},
+ numvertexids = 0,
+ vertexpairs1 = {},
+ vertexpairs2 = {},
+ pairconnected = {},
+ edgepairsvertex = {},
+ edgepairsedge = {},
+ edgevertex1 = {},
+ edgevertex2 = {},
+ edgedeprecated = {},
+ subdivisionedges = {},
+ subdivisionvertices = {},
+ temperature = 1,
+ }
+
+ setmetatable(t, PDP)
+ return t
+end
+
+function PDP:run()
+ self:normalize_size()
+ self:find_force_pairs()
+
+ local delta = self.delta
+ local gamma = self.gamma
+ local coolingfactor = self.coolingfactor
+ local expiterations = self.expiterations
+ local startrepexp = self.startrepexp
+ local endattexp = self.endattexp
+ local startattexp = self.startattexp
+ local endrepexp = self.endrepexp
+
+ local vertexpairs1 = self.vertexpairs1
+ local vertexpairs2 = self.vertexpairs2
+ local pairconnected = self.pairconnected
+ local edgepairsvertex = self.edgepairsvertex
+ local edgepairsedge = self.edgepairsedge
+ local edgevertex1 = self.edgevertex1
+ local edgevertex2 = self.edgevertex2
+ local edgedeprecated = self.edgedeprecated
+
+ local forcexs = {}
+ local forceys = {}
+ local posxs = self.posxs
+ local posys = self.posys
+ local cvsxs = self.cvsxs
+ local cvsys = self.cvsys
+ local numcvs = {}
+ for i, v in ipairs(self.embedding.vertices) do
+ cvsxs[i] = {}
+ cvsys[i] = {}
+ posxs[i] = v.inputvertex.pos.x
+ posys[i] = v.inputvertex.pos.y
+ end
+
+ local numorigvertices = self.numvertexids
+ local numorigedges = self.numedgeids
+ local numdivisions = self.numdivisions
+ local divdelta = delta / (numdivisions + 1)
+ local stresscounter = {}
+ for i = 1, self.numedgeids do
+ stresscounter[i] = 0
+ end
+
+ local appthreshold = self.appthreshold
+ local stretchthreshold = self.stretchthreshold
+ local stresscounterthreshold = self.stresscounterthreshold
+
+ for i = 1, numorigedges do
+ local iv1 = self.embedding.vertices[edgevertex1[i]].inputvertex
+ local iv2 = self.embedding.vertices[edgevertex2[i]].inputvertex
+ local arc = self.ugraph:arc(iv1, iv2)
+ --TODO subdivide edge if desired
+ --self:subdivide_edge(i)
+ end
+
+ -- main loop
+ local iteration = 0
+ repeat
+ iteration = iteration + 1
+ local temperature = self.temperature
+ local ratio = math.min(1, iteration / expiterations)
+ local repexp = startrepexp + (endrepexp - startrepexp) * ratio
+ local attexp = startattexp + (endattexp - startattexp) * ratio
+ for i = 1, self.numvertexids do
+ forcexs[i] = 0
+ forceys[i] = 0
+ numcvs[i] = 0
+ end
+ -- vertex-vertex forces
+ for i = 1, #vertexpairs1 do
+ local id1 = vertexpairs1[i]
+ local id2 = vertexpairs2[i]
+ local diffx = posxs[id2] - posxs[id1]
+ local diffy = posys[id2] - posys[id1]
+ local dist2 = diffx * diffx + diffy * diffy
+ local dist = math.sqrt(dist2)
+ local dirx = diffx / dist
+ local diry = diffy / dist
+ assert(dist ~= 0)
+
+ local useddelta = delta
+ local hasdivvertex = id1 > numorigvertices or id2 > numorigvertices
+
+ -- calculate attractive force
+ if pairconnected[i] then
+ if hasdivvertex then
+ useddelta = divdelta
+ end
+ local mag = (dist / useddelta) ^ attexp * useddelta
+ local fax = mag * dirx
+ local fay = mag * diry
+ forcexs[id1] = forcexs[id1] + fax
+ forceys[id1] = forceys[id1] + fay
+ forcexs[id2] = forcexs[id2] - fax
+ forceys[id2] = forceys[id2] - fay
+ elseif hasdivvertex then
+ useddelta = gamma
+ end
+
+ -- calculate repulsive force
+ local mag = (useddelta / dist) ^ repexp * useddelta
+ local frx = mag * dirx
+ local fry = mag * diry
+ forcexs[id1] = forcexs[id1] - frx
+ forceys[id1] = forceys[id1] - fry
+ forcexs[id2] = forcexs[id2] + frx
+ forceys[id2] = forceys[id2] + fry
+ end
+
+ -- edge-vertex forces and collisions
+ for i = 1, #edgepairsvertex do
+ local edgeid = edgepairsedge[i]
+ if not edgedeprecated[edgeid] then
+ local id1 = edgepairsvertex[i]
+ local id2 = edgevertex1[edgeid]
+ local id3 = edgevertex2[edgeid]
+ assert(id2 ~= id1 and id3 ~= id1)
+
+ local abx = posxs[id3] - posxs[id2]
+ local aby = posys[id3] - posys[id2]
+ local dab2 = abx * abx + aby * aby
+ local dab = math.sqrt(dab2)
+ assert(dab ~= 0)
+ local abnx = abx / dab
+ local abny = aby / dab
+ local avx = posxs[id1] - posxs[id2]
+ local avy = posys[id1] - posys[id2]
+ local daiv = abnx * avx + abny * avy
+ local ivx = posxs[id2] + abnx * daiv
+ local ivy = posys[id2] + abny * daiv
+ local vivx = ivx - posxs[id1]
+ local vivy = ivy - posys[id1]
+ local dviv2 = vivx * vivx + vivy * vivy
+ local dviv = math.sqrt(dviv2)
+ local afactor, bfactor = 1, 1
+ local cvx
+ local cvy
+ if daiv < 0 then
+ cvx = -avx / 2
+ cvy = -avy / 2
+ local norm2 = cvx * cvx + cvy * cvy
+ bfactor = 1 + (cvx * abx + cvy * aby) / norm2
+ elseif daiv > dab then
+ cvx = (abx - avx) / 2
+ cvy = (aby - avy) / 2
+ local norm2 = cvx * cvx + cvy * cvy
+ afactor = 1 - (cvx * abx + cvy * aby) / norm2
+ else
+ if edgeid < numorigedges
+ and dviv < gamma * appthreshold
+ and dab > delta * stretchthreshold then
+ stresscounter[edgeid] = stresscounter[edgeid] + 1
+ end
+ assert(dviv > 0)
+ cvx = vivx / 2
+ cvy = vivy / 2
+ -- calculate edge repulsive force
+ local dirx = -vivx / dviv
+ local diry = -vivy / dviv
+ local mag = (gamma / dviv) ^ repexp * gamma
+ local fex = mag * dirx
+ local fey = mag * diry
+ local abratio = daiv / dab
+ forcexs[id1] = forcexs[id1] + fex
+ forceys[id1] = forceys[id1] + fey
+ forcexs[id2] = forcexs[id2] - fex * (1 - abratio)
+ forceys[id2] = forceys[id2] - fey * (1 - abratio)
+ forcexs[id3] = forcexs[id3] - fex * abratio
+ forceys[id3] = forceys[id3] - fey * abratio
+ end
+ local nv = numcvs[id1] + 1
+ local na = numcvs[id2] + 1
+ local nb = numcvs[id3] + 1
+ numcvs[id1] = nv
+ numcvs[id2] = na
+ numcvs[id3] = nb
+ cvsxs[id1][nv] = cvx
+ cvsys[id1][nv] = cvy
+ cvsxs[id2][na] = -cvx * afactor
+ cvsys[id2][na] = -cvy * afactor
+ cvsxs[id3][nb] = -cvx * bfactor
+ cvsys[id3][nb] = -cvy * bfactor
+ end
+ end
+
+ -- clamp forces
+ local scalefactor = 1
+ local collision = false
+ for i = 1, self.numvertexids do
+ local forcex = forcexs[i]
+ local forcey = forceys[i]
+ forcex = forcex * temperature
+ forcey = forcey * temperature
+ forcexs[i] = forcex
+ forceys[i] = forcey
+ local forcenorm2 = forcex * forcex + forcey * forcey
+ local forcenorm = math.sqrt(forcenorm2)
+ scalefactor = math.min(scalefactor, delta * 3 * temperature / forcenorm)
+ local cvys = cvsys[i]
+ for j, cvx in ipairs(cvsxs[i]) do
+ local cvy = cvys[j]
+ local cvnorm2 = cvx * cvx + cvy * cvy
+ local cvnorm = math.sqrt(cvnorm2)
+ local projforcenorm = (cvx * forcex + cvy * forcey) / cvnorm
+ if projforcenorm > 0 then
+ local factor = cvnorm * 0.9 / projforcenorm
+ if factor < scalefactor then
+ scalefactor = factor
+ collision = true
+ end
+ end
+ end
+ end
+ local moved = false
+ for i = 1, self.numvertexids do
+ local forcex = forcexs[i] * scalefactor
+ local forcey = forceys[i] * scalefactor
+ posxs[i] = posxs[i] + forcex
+ posys[i] = posys[i] + forcey
+ local forcenorm2 = forcex * forcex + forcey * forcey
+ if forcenorm2 > 0.0001 * delta * delta then moved = true end
+ end
+
+ -- subdivide stressed edges
+ if numdivisions > 0 then
+ for i = 1, numorigedges do
+ if stresscounter[i] > stresscounterthreshold then
+ self:subdivide_edge(i)
+ stresscounter[i] = 0
+ end
+ end
+ end
+ self.temperature = self.temperature * coolingfactor
+ until not collision and not moved
+ print("\nfinished PDP after " .. iteration .. " iterations")
+
+ -- write the positions back
+ for i, v in ipairs(self.embedding.vertices) do
+ v.inputvertex.pos.x = posxs[i]
+ v.inputvertex.pos.y = posys[i]
+ end
+
+ -- route the edges
+ for i = 1, self.numedgeids do
+ if self.subdivisionvertices[i] then
+ local iv1 = self.embedding.vertices[self.edgevertex1[i]].inputvertex
+ local iv2 = self.embedding.vertices[self.edgevertex2[i]].inputvertex
+ local arc = self.ugraph:arc(iv1, iv2)
+ local p = Path.new()
+ p:appendMoveto(arc.tail.pos:clone())
+ for _, vid in ipairs(self.subdivisionvertices[i]) do
+ p:appendLineto(self.posxs[vid], self.posys[vid])
+ end
+ p:appendLineto(arc.head.pos:clone())
+ arc.path = p
+ end
+ end
+end
+
+function PDP:subdivide_edge(edgeid)
+ assert(self.subdivisionedges[edgeid] == nil)
+ local numdivisions = self.numdivisions
+ local subdivisionedges = {}
+ local subdivisionvertices = {}
+ local id1 = self.edgevertex1[edgeid]
+ local id2 = self.edgevertex2[edgeid]
+ local x1 = self.posxs[id1]
+ local y1 = self.posys[id1]
+ local x2 = self.posxs[id2]
+ local y2 = self.posys[id2]
+ local prevvertexid = id1
+ for i = 1, numdivisions do
+ -- create new edge and vertex
+ local newvertexid1 = self.numvertexids + i
+ table.insert(subdivisionvertices, newvertexid1)
+ self.posxs[newvertexid1] = (x1 * (numdivisions + 1 - i) + x2 * i)
+ / (numdivisions + 1)
+ self.posys[newvertexid1] = (y1 * (numdivisions + 1 - i) + y2 * i)
+ / (numdivisions + 1)
+ self.cvsxs[newvertexid1] = {}
+ self.cvsys[newvertexid1] = {}
+
+ local newedgeid = self.numedgeids + i
+ table.insert(subdivisionedges, newedgeid)
+ table.insert(self.edgevertex1, prevvertexid)
+ table.insert(self.edgevertex2, newvertexid1)
+ prevvertexid = newvertexid1
+
+ -- pair the new vertex
+ -- with first vertex of the edge being divided
+ table.insert(self.vertexpairs1, self.edgevertex1[edgeid])
+ table.insert(self.vertexpairs2, newvertexid1)
+ table.insert(self.pairconnected, i == 1)
+
+ -- with second vertex of the edge being divided
+ table.insert(self.vertexpairs1, self.edgevertex2[edgeid])
+ table.insert(self.vertexpairs2, newvertexid1)
+ table.insert(self.pairconnected, i == numdivisions)
+
+ -- with each other
+ for j = i + 1, numdivisions do
+ local newvertexid2 = self.numvertexids + j
+ table.insert(self.vertexpairs1, newvertexid1)
+ table.insert(self.vertexpairs2, newvertexid2)
+ table.insert(self.pairconnected, j == i + 1)
+ end
+
+ -- with new edges
+ -- before vertex
+ for j = 1, i - 1 do
+ local newedgeid = self.numedgeids + j
+ table.insert(self.edgepairsvertex, newvertexid1)
+ table.insert(self.edgepairsedge, newedgeid)
+ end
+ -- after vertex
+ for j = i + 2, numdivisions + 1 do
+ local newedgeid = self.numedgeids + j
+ table.insert(self.edgepairsvertex, newvertexid1)
+ table.insert(self.edgepairsedge, newedgeid)
+ end
+
+ -- pair the new edges with vertices of the edge being divided
+ if i > 1 then
+ table.insert(self.edgepairsvertex, id1)
+ table.insert(self.edgepairsedge, newedgeid)
+ end
+ table.insert(self.edgepairsvertex, id2)
+ table.insert(self.edgepairsedge, newedgeid)
+ end
+ -- create last edge
+ table.insert(subdivisionedges, self.numedgeids + numdivisions + 1)
+ table.insert(self.edgevertex1, prevvertexid)
+ table.insert(self.edgevertex2, id2)
+
+ -- pair last edge with first vertex of the edge being divided
+ table.insert(self.edgepairsvertex, id1)
+ table.insert(self.edgepairsedge, self.numedgeids + numdivisions + 1)
+
+ self.subdivisionedges[edgeid] = subdivisionedges
+ self.subdivisionvertices[edgeid] = subdivisionvertices
+
+ -- pair new edges and vertices with existing edges and vertices
+ local sameface = false
+ local start = self.embeddingedges[edgeid]
+ local twin = start.twin
+ local donevertices = { [start.target] = true, [twin.target] = true }
+ local doneedges = { [start] = true, [twin] = true }
+ local current = start.twin.links[1]
+ for twice = 1, 2 do
+ while current ~= start do
+ if current == twin then
+ sameface = true
+ end
+
+ -- pair edge with the new vertices
+ -- or pair subdivision of edge with new vertices and edges
+ if not doneedges[current] then
+ local currentedgeid = self.edgeids[current]
+ if self.subdivisionvertices[currentedgeid] then
+ for _, vid in ipairs(self.subdivisionvertices[currentedgeid]) do
+ for i = 1, numdivisions do
+ local newvertexid = self.numvertexids + i
+ table.insert(self.vertexpairs1, vid)
+ table.insert(self.vertexpairs2, newvertexid)
+ self.pairconnected[#self.vertexpairs1] = false
+ end
+ for i = 1, numdivisions + 1 do
+ local newedgeid = self.numedgeids + i
+ table.insert(self.edgepairsvertex, vid)
+ table.insert(self.edgepairsedge, newedgeid)
+ end
+ end
+ for _, eid in ipairs(self.subdivisionedges[currentedgeid]) do
+ for i = 1, numdivisions do
+ local newvertexid = self.numvertexids + i
+ table.insert(self.edgepairsvertex, newvertexid)
+ table.insert(self.edgepairsedge, eid)
+ end
+ end
+ else
+ for i = 1, numdivisions do
+ local newvertexid = self.numvertexids + i
+ table.insert(self.edgepairsvertex, newvertexid)
+ table.insert(self.edgepairsedge, currentedgeid)
+ end
+ end
+ doneedges[current] = true
+ end
+
+ -- pair target vertex with the new vertices and edges
+ local vertexid = self.vertexids[current.target]
+ if not donevertices[current.target] then
+ for i = 1, numdivisions do
+ local newvertexid = self.numvertexids + i
+ table.insert(self.vertexpairs1, vertexid)
+ table.insert(self.vertexpairs2, newvertexid)
+ self.pairconnected[#self.vertexpairs1] = false
+ end
+ for i = 1, numdivisions + 1 do
+ local newedgeid = self.numedgeids + i
+ table.insert(self.edgepairsvertex, vertexid)
+ table.insert(self.edgepairsedge, newedgeid)
+ end
+ end
+ current = current.twin.links[1]
+ end
+ start = self.embeddingedges[edgeid].twin
+ current = start.twin.links[1]
+ if sameface then
+ break
+ end
+ end
+
+ self.edgedeprecated[edgeid] = true
+ self.numvertexids = self.numvertexids + numdivisions
+ self.numedgeids = self.numedgeids + numdivisions + 1
+end
+
+function PDP:find_force_pairs()
+ local donevertices = {}
+ -- number all vertices
+ local vertexids = self.vertexids
+ for i, v in ipairs(self.embedding.vertices) do
+ vertexids[v] = i
+ end
+ self.numvertexids = #self.embedding.vertices
+
+ local edgeids = self.edgeids
+ local numedgeids = 0
+ -- number all edges
+ for _, v in ipairs(self.embedding.vertices) do
+ local id = vertexids[v]
+ local start = v.link
+ local current = start
+ repeat
+ local targetid = vertexids[current.target]
+ if edgeids[current] == nil then
+ table.insert(self.edgevertex1, id)
+ table.insert(self.edgevertex2, targetid)
+ numedgeids = numedgeids + 1
+ edgeids[current] = numedgeids
+ edgeids[current.twin] = numedgeids
+ self.embeddingedges[numedgeids] = current
+ end
+ current = current.links[0]
+ until current == start
+ end
+
+ -- find all force pairs
+ for _, v in ipairs(self.embedding.vertices) do
+ local id = vertexids[v]
+ donevertices[id] = true
+ local vertexset = {}
+ local edgeset = {}
+ local start = v.link
+ repeat
+ local targetid = vertexids[start.target]
+ if vertexset[targetid] == nil and not donevertices[targetid] then
+ table.insert(self.pairconnected, true)
+ table.insert(self.vertexpairs1, id)
+ table.insert(self.vertexpairs2, targetid)
+ vertexset[targetid] = true
+ end
+ local current = start.twin.links[1]
+ while current.target ~= v do
+ local targetid = vertexids[current.target]
+ if vertexset[targetid] == nil and not donevertices[targetid] then
+ table.insert(self.pairconnected, self.ugraph:arc(v.inputvertex, current.target.inputvertex) ~= nil)
+ table.insert(self.vertexpairs1, id)
+ table.insert(self.vertexpairs2, targetid)
+ vertexset[targetid] = true
+ end
+ if edgeset[current] == nil then
+ table.insert(self.edgepairsvertex, id)
+ table.insert(self.edgepairsedge, edgeids[current])
+ edgeset[current] = true
+ edgeset[current.twin] = true
+ end
+ current = current.twin.links[1]
+ end
+ start = start.links[0]
+ until start == v.link
+ end
+
+ self.numedgeids = numedgeids
+end
+
+function PDP:normalize_size()
+ local minx = math.huge
+ local maxx = -math.huge
+ local miny = math.huge
+ local maxy = -math.huge
+
+ for _, v in ipairs(self.ugraph.vertices) do
+ minx = math.min(minx, v.pos.x)
+ maxx = math.max(maxx, v.pos.x)
+ miny = math.min(miny, v.pos.y)
+ maxy = math.max(maxy, v.pos.y)
+ end
+
+ local area = (maxx - minx) * (maxy - miny)
+ local gridarea = #self.ugraph.vertices * self.delta * self.delta
+
+ local scale = math.sqrt(gridarea) / math.sqrt(area)
+
+ for _, v in ipairs(self.ugraph.vertices) do
+ v.pos = v.pos * scale
+ end
+end
+
+-- done
+
+return PDP
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/planar/PlanarLayout.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/planar/PlanarLayout.lua
new file mode 100644
index 0000000000..9e5b8d72ec
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/planar/PlanarLayout.lua
@@ -0,0 +1,159 @@
+
+
+local PlanarLayout = {}
+require("pgf.gd.planar").PlanarLayout = PlanarLayout
+
+-- imports
+local Coordinate = require "pgf.gd.model.Coordinate"
+local Storage = require "pgf.gd.lib.Storage"
+local BoyerMyrvold = require "pgf.gd.planar.BoyerMyrvold2004"
+local ShiftMethod = require "pgf.gd.planar.ShiftMethod"
+local Embedding = require "pgf.gd.planar.Embedding"
+local PDP = require "pgf.gd.planar.PDP"
+local InterfaceToAlgorithms = require("pgf.gd.interface.InterfaceToAlgorithms")
+local createEdge = InterfaceToAlgorithms.createEdge
+local createVertex = InterfaceToAlgorithms.createVertex
+
+InterfaceToAlgorithms.declare {
+ key = "planar layout",
+ algorithm = PlanarLayout,
+ preconditions = {
+ connected = true,
+ loop_free = true,
+ simple = true,
+ },
+ postconditions = {
+ fixed = true,
+ },
+ summary = [["
+ The planar layout draws planar graphs without edge crossings.
+ "]],
+ documentation = [["
+ The planar layout is a pipeline of algorithms to produce
+ a crossings-free drawing of a planar graph.
+ First a combinatorical embedding of the graph is created using
+ the Algorithm from Boyer and Myrvold.
+ The combinatorical Embedding is then being improved by
+ by the Sort and Flip algorithm and triangulated afterwards.
+ To determine the actual node positions the shift method
+ by de Fraysseix, Pach and Pollack is used.
+ Finally the force based Planar Drawing Postprocessing improves the drawing.
+ "]],
+ examples = {
+ [["
+ \tikz \graph [nodes={draw, circle}] {
+ a -- {
+ b -- {
+ d -- i,
+ e,
+ f
+ },
+ c -- {
+ g,
+ h
+ }
+ },
+ f --[no span edge] a,
+ h --[no span edge] a,
+ i --[no span edge] g,
+ f --[no span edge] g,
+ c --[no span edge] d,
+ e --[no span edge] c
+ }
+ "]]
+ }
+}
+
+function PlanarLayout:run()
+ --local file = io.open("timing.txt", "a")
+
+ local options = self.digraph.options
+
+ -- get embedding
+ local bm = BoyerMyrvold.new()
+ bm:init(self.ugraph)
+ local embedding = bm:run()
+
+ assert(embedding, "Graph is not planar")
+
+ --local start = os.clock()
+ if options["use sf"] then
+ embedding:improve()
+ end
+
+ -- choose external face
+ local exedge, exsize = embedding:get_biggest_face()
+
+ -- surround graph with triangle
+ local v1, v2, vn = embedding:surround_by_triangle(exedge, exsize)
+
+ -- make maximal planar
+ embedding:triangulate()
+
+ if options["show virtual"] then
+ -- add virtual vertices to input graph
+ for _, vertex in ipairs(embedding.vertices) do
+ if vertex.virtual then
+ vertex.inputvertex = createVertex(self, {
+ name = nil,--vertex.name,
+ generated_options = {},
+ text = vertex.name
+ })
+ vertex.virtual = false
+ end
+ end
+
+ -- add virtual edges to input graph
+ for _, vertex in ipairs(embedding.vertices) do
+ for halfedge in Embedding.adjacency_iterator(vertex.link) do
+ if halfedge.virtual then
+ createEdge(
+ self,
+ vertex.inputvertex,
+ halfedge.target.inputvertex
+ )
+ end
+ halfedge.virtual = false
+ end
+ end
+ end
+
+ -- create canonical ordering
+ local order = embedding:canonical_order(v1, v2, vn)
+
+ local sm = ShiftMethod.new()
+ sm:init(order)
+ local gridpos = sm:run()
+
+ local gridspacing = options["grid spacing"]
+ for _, v in ipairs(order) do
+ if not v.virtual then
+ local iv = v.inputvertex
+ iv.pos.x = gridpos[v].x * gridspacing
+ iv.pos.y = gridpos[v].y * gridspacing
+ end
+ end
+
+ embedding:remove_virtual()
+
+ --start = os.clock()
+ if options["use pdp"] then
+ local pdp = PDP.new(
+ self.ugraph, embedding,
+ options["node distance"],
+ options["node distance"],
+ options["pdp cooling factor"],
+ options["exponent change iterations"],
+ options["start repulsive exponent"],
+ options["end repulsive exponent"],
+ options["start attractive exponent"],
+ options["end attractive exponent"],
+ options["edge approach threshold"],
+ options["edge stretch threshold"],
+ options["stress counter threshold"],
+ options["edge divisions"]
+ )
+ pdp:run()
+ end
+
+end
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/planar/ShiftMethod.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/planar/ShiftMethod.lua
new file mode 100644
index 0000000000..c568ca5696
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/planar/ShiftMethod.lua
@@ -0,0 +1,128 @@
+local SM = {}
+require("pgf.gd.planar").ShiftMethod = SM
+
+-- imports
+local Embedding = require("pgf.gd.planar.Embedding")
+
+-- create class properties
+SM.__index = SM
+
+function SM.new()
+ local t = {}
+ setmetatable(t, SM)
+ return t
+end
+
+function SM:init(vertices)
+ self.vertices = vertices
+ self.xoff = {}
+ self.pos = {}
+ for _, v in ipairs(vertices) do
+ self.pos[v] = {}
+ end
+ self.left = {}
+ self.right = {}
+end
+
+function SM:run()
+ local v1 = self.vertices[1]
+ local v2 = self.vertices[2]
+ local v3 = self.vertices[3]
+
+ self.xoff[v1] = 0
+ self.pos[v1].y = 0
+ self.right[v1] = v3
+
+ self.xoff[v3] = 1
+ self.pos[v3].y = 1
+ self.right[v3] = v2
+
+ self.xoff[v2] = 1
+ self.pos[v2].y = 0
+
+ local n = #self.vertices
+ for k = 4, n do
+ local vk = self.vertices[k]
+ local wplink, wqlink, wp1qsum
+ if k ~= n then
+ wplink, wqlink, wp1qsum = self:get_attachments(vk)
+ else
+ wplink, wqlink, wp1qsum = self:get_last_attachments(vk, v1, v2)
+ end
+ local wp, wq = wplink.target, wqlink.target
+ local wp1 = wplink.links[0].target
+ local wq1 = wqlink.links[1 - 0].target
+ self.xoff[wp1] = self.xoff[wp1] + 1
+ self.xoff[wq] = self.xoff[wq] + 1
+ wp1qsum = wp1qsum + 2
+ self.xoff[vk] = (wp1qsum + self.pos[wq].y - self.pos[wp].y) / 2
+ self.pos[vk].y = (wp1qsum + self.pos[wq].y + self.pos[wp].y) / 2
+ -- = self.xoff[vk] + self.pos[wp].y ?
+ self.right[wp] = vk
+ if wp ~= wq1 then
+ self.left[vk] = wp1
+ self.right[wq1] = nil
+ self.xoff[wp1] = self.xoff[wp1] - self.xoff[vk]
+ end
+ self.right[vk] = wq
+ self.xoff[wq] = wp1qsum - self.xoff[vk]
+ end
+ self.pos[v1].x = 0
+ self:accumulate_offset(v1, 0)
+ return self.pos
+end
+
+function SM:get_attachments(vk)
+ local wplink, wqlink
+ local wp1qsum = 0
+ local start = vk.link
+ local startattach = self.xoff[start.target] ~= nil
+ local current = start.links[0]
+ local last = start
+ repeat
+ local currentattach = self.xoff[current.target] ~= nil
+ local lastattach = self.xoff[last.target] ~= nil
+ if currentattach ~= lastattach then
+ if currentattach then
+ wplink = current
+ else
+ wqlink = last
+ end
+ if currentattach == startattach and not startattach then
+ break
+ end
+ currentattach = lastattach
+ elseif currentattach then
+ wp1qsum = wp1qsum + self.xoff[current.target]
+ end
+ last = current
+ current = current.links[0]
+ until last == start
+ return wplink, wqlink, wp1qsum
+end
+
+function SM:get_last_attachments(vn, v1, v2)
+ local wplink, wqlink
+ local wp1qsum = 0
+ for halfedge in Embedding.adjacency_iterator(vn.link, ccwdir) do
+ local target = halfedge.target
+ if target == v1 then
+ wplink = halfedge
+ elseif target == v2 then
+ wqlink = halfedge
+ end
+ wp1qsum = wp1qsum + self.xoff[target]
+ end
+ return wplink, wqlink, wp1qsum
+end
+
+function SM:accumulate_offset(v, x)
+ x = x + self.xoff[v]
+ self.pos[v].x = x
+ local l = self.left[v]
+ local r = self.right[v]
+ if l then self:accumulate_offset(l, x) end
+ if r then self:accumulate_offset(r, x) end
+end
+
+return SM
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/planar/library.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/planar/library.lua
new file mode 100644
index 0000000000..68c46898e3
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/planar/library.lua
@@ -0,0 +1,2 @@
+require "pgf.gd.planar.PlanarLayout"
+require "pgf.gd.planar.parameters"
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/planar/parameters.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/planar/parameters.lua
new file mode 100644
index 0000000000..603f2ee7cc
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/planar/parameters.lua
@@ -0,0 +1,144 @@
+-- Imports
+local declare = require("pgf.gd.interface.InterfaceToAlgorithms").declare
+
+declare {
+ key = "use pdp",
+ type = "boolean",
+ initial = "true",
+
+ summary = [["
+ Whether or not to use the Planar Drawing Postprocessing
+ to improve the drawing.
+ "]]
+}
+
+declare {
+ key = "use sf",
+ type = "boolean",
+ initial = "true",
+
+ summary = [["
+ Whether or not to use the Sort and Flip Algorithm
+ to improve the combinatorical embedding.
+ "]]
+}
+
+declare {
+ key = "grid spacing",
+ type = "number",
+ initial = "10",
+
+ summary = [["
+ If the |use pdp| option is not set,
+ this sets the spacing of the grid used by the shift method.
+ A bigger grid spacing will result in a bigger drawing.
+ "]]
+}
+
+declare {
+ key = "pdp cooling factor",
+ type = "number",
+ initial = "0.98",
+
+ summary = [["
+ This sets the cooling factor used by the Planar Drawing Postprocessing.
+ A higher cooling factor can result in better quality of the drawing,
+ but will increase the run time of the algorithm.
+ "]]
+}
+
+declare {
+ key = "start repulsive exponent",
+ type = "number",
+ initial = "2",
+
+ summary = [["
+ Start value of the exponent used in the calculation of all repulsive forces in PDP
+ "]]
+}
+
+declare {
+ key = "end repulsive exponent",
+ type = "number",
+ initial = "2",
+
+ summary = [["
+ End value of the exponent used in the calculation of all repulsive forces in PDP.
+ "]]
+}
+
+declare {
+ key = "start attractive exponent",
+ type = "number",
+ initial = "2",
+
+ summary = [["
+ Start value of the exponent used in PDP's calculation of the attractive force between
+ nodes connected by an edge.
+ "]]
+}
+
+declare {
+ key = "end attractive exponent",
+ type = "number",
+ initial = "2",
+
+ summary = [["
+ End value of the exponent used in PDP's calculation of the attractive force between
+ nodes connected by an edge.
+ "]]
+}
+
+declare {
+ key = "exponent change iterations",
+ type = "number",
+ initial = "1",
+
+ summary = [["
+ The number of iterations over which to modify the force exponents.
+ In iteration one the exponents will have their start value and in iteration
+ |exponent change iterations| they will have their end value.
+ "]]
+}
+
+declare {
+ key = "edge approach threshold",
+ type = "number",
+ initial = "0.3",
+
+ summary = [["
+ The maximum ration between the actual and the desired node-edge distance
+ which is required to count an edge as stressed.
+ "]]
+}
+
+declare {
+ key = "edge stretch threshold",
+ type = "number",
+ initial = "1.5",
+
+ summary = [["
+ The minimum ration between the actual and the desired edge length
+ which is required to count an edge as stressed.
+ "]]
+}
+
+declare {
+ key = "stress counter threshold",
+ type = "number",
+ initial = "30",
+
+ summary = [["
+ The number of iterations an edge has to be under stress before it will be subdivided.
+ "]]
+}
+
+declare {
+ key = "edge divisions",
+ type = "number",
+ initial = "0",
+
+ summary = [["
+ The number of edges in which stressed edges will be subdivided.
+ "]]
+}
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/routing.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/routing.lua
new file mode 100644
index 0000000000..77166c5cbf
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/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$
+
+
+
+local routing = {}
+
+-- Declare namespace
+require("pgf.gd").routing = routing
+
+
+-- Done
+
+return routing \ No newline at end of file
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/routing/Hints.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/routing/Hints.lua
new file mode 100644
index 0000000000..7d34726f47
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/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$
+
+
+---
+-- 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 algorithms 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/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/routing/NecklaceRouting.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/routing/NecklaceRouting.lua
new file mode 100644
index 0000000000..defcf9f8c6
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/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$
+
+
+-- 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 routing| 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
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/routing/library.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/routing/library.lua
new file mode 100644
index 0000000000..17c959d685
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/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$
+
+
+-- 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/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/tools/make_gd_wrap.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/tools/make_gd_wrap.lua
new file mode 100644
index 0000000000..a0aee6501c
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/tools/make_gd_wrap.lua
@@ -0,0 +1,183 @@
+-- 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$
+
+
+
+---
+-- This program generates a C wrap file around graph drawing
+-- algorithms. The idea is that when you have a graph drawing
+-- algorithm implemented in C and wish to invoke it from Lua, you need
+-- a wrapper that manages the translation between Lua and C. This
+-- program is intended to make it (reasonably) easy to produce such a
+-- wrapper.
+
+
+
+-- Sufficient number of arguments?
+
+if #arg < 4 or arg[1] == "-h" or arg[1] == "-?" or arg[1] == "--help" then
+ print([["
+Usage: make_gd_wrap library1 library2 ... libraryn template library_name target_c_file
+
+This program will read all of the graph drawing library files using
+Lua's require. Then, it will iterate over all declared algorithm keys
+(declared using declare { algorithm_written_in_c = ... }) and will
+produce the code for library for the required target C files based on
+the template.
+"]])
+ os.exit()
+end
+
+
+-- Imports
+
+local InterfaceToDisplay = require "pgf.gd.interface.InterfaceToDisplay"
+local InterfaceCore = require "pgf.gd.interface.InterfaceCore"
+
+
+-- Ok, setup:
+
+InterfaceToDisplay.bind(require "pgf.gd.bindings.Binding")
+
+
+-- Now, read all libraries:
+
+for i=1,#arg-3 do
+ require(arg[i])
+end
+
+
+-- Now, read the template:
+
+local file = io.open(arg[#arg-2])
+local template = file:read("*a")
+file:close()
+
+-- Let us grab the declaration:
+
+local functions_dec = (template:match("%$functions(%b{})") or ""):match("^{(.*)}$")
+local functions_reg_dec = (template:match("%$functions_registry(%b{})") or ""):match("^{(.*)}$")
+local factories_dec = (template:match("%$factories(%b{})") or ""):match("^{(.*)}$")
+local factories_reg_dec = (template:match("%$factories_registry(%b{})") or ""):match("^{(.*)}$")
+
+-- Now, handle all keys with a algorithm_written_in_c field
+
+local keys = InterfaceCore.keys
+local filename = arg[#arg]
+local target = arg[#arg-1]
+
+local includes = {}
+local functions = {}
+local functions_registry = {}
+
+local factories = {}
+local factories_reg = {}
+
+for _,k in ipairs(keys) do
+
+ if k.algorithm_written_in_c and k.code then
+
+ local library, fun_name = k.algorithm_written_in_c:match("(.*)%.(.*)")
+
+ if target == library then
+ -- First, gather the includes:
+ if type(k.includes) == "string" then
+ if not includes[k.includes] then
+ includes[#includes + 1] = k.includes
+ includes[k.includes] = true
+ end
+ elseif type(k.includes) == "table" then
+ for _,i in ipairs(k.includes) do
+ if not includes[i] then
+ includes[#includes + 1] = i
+ includes[i] = true
+ end
+ end
+ end
+
+ -- Second, create a code block:
+ functions[#functions+1] = functions_dec:gsub("%$([%w_]-)%b{}",
+ {
+ function_name = fun_name,
+ function_body = k.code
+ })
+
+ -- Third, create functions_registry entry
+ functions_registry[#functions_registry + 1] = functions_reg_dec:gsub("%$([%w_]-)%b{}",
+ {
+ function_name = fun_name,
+ function_body = k.code
+ })
+ end
+ end
+
+
+ if k.module_class then
+
+ -- First, gather the includes:
+ if type(k.includes) == "string" then
+ if not includes[k.includes] then
+ includes[#includes + 1] = k.includes
+ includes[k.includes] = true
+ end
+ elseif type(k.includes) == "table" then
+ for _,i in ipairs(k.includes) do
+ if not includes[i] then
+ includes[#includes + 1] = i
+ includes[i] = true
+ end
+ end
+ end
+
+ -- Second, create a code block:
+ factories[#factories+1] = factories_dec:gsub(
+ "%$([%w_]-)%b{}",
+ {
+ factory_class = k.module_class,
+ factory_code = k.code,
+ factory_base = k.module_base,
+ factory_name = k.module_class .. '_factory'
+ })
+
+ -- Third, create factories_registry entry
+ factories_reg[#factories_reg + 1] = factories_reg_dec:gsub(
+ "%$([%w_]-)%b{}",
+ {
+ factory_class = k.module_class,
+ factory_code = k.code,
+ factory_base = k.module_base,
+ factory_name = k.module_class .. '_factory'
+ })
+ end
+end
+
+
+local file = io.open(filename, "w")
+
+if not file then
+ print ("failed to open file " .. filename)
+ os.exit(-1)
+end
+
+file:write ((template:gsub(
+ "%$([%w_]-)%b{}",
+ {
+ factories = table.concat(factories, "\n\n"),
+ factories_registry = table.concat(factories_reg, "\n"),
+ functions = table.concat(functions, "\n\n"),
+ functions_registry = table.concat(functions_registry, "\n"),
+ includes = table.concat(includes, "\n"),
+ library_c_name = target:gsub("%.", "_"),
+ library_name = target
+ })))
+file:close()
+
+
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/trees.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/trees.lua
new file mode 100644
index 0000000000..8c45e912dc
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/trees.lua
@@ -0,0 +1,21 @@
+-- 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$
+
+
+local trees = {}
+
+-- Declare namespace
+require("pgf.gd").trees = tree
+
+
+-- Done
+
+return trees \ No newline at end of file
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/trees/ChildSpec.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/trees/ChildSpec.lua
new file mode 100644
index 0000000000..cc3c18d12c
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/trees/ChildSpec.lua
@@ -0,0 +1,226 @@
+-- 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$
+
+
+
+---
+-- @section subsection {Specifying Missing Children}
+-- \label{section-gd-missing-children}
+--
+-- In the present section we discuss keys for specifying missing children
+-- in a tree. For many certain kind of trees, in particular for binary
+-- trees, there are not just ``a certain number of children'' at each
+-- node, but, rather, there is a designated ``first'' (or ``left'') child
+-- and a ``second'' (or ``right'') child. Even if one of these children
+-- is missing and a node actually has only one child, the single child will
+-- still be a ``first'' or ``second'' child and this information should
+-- be taken into consideration when drawing a tree.
+--
+-- The first useful key for specifying missing children is
+-- |missing number of children| which allows you to state how many
+-- children there are, at minimum.
+--
+-- Once the minimum number of children has been set, we still need a way
+-- of specifying ``missing first children'' or, more generally, missing
+-- children that are not ``at the end'' of the list of children. For
+-- this, there are three methods:
+-- %
+-- \begin{enumerate}
+-- \item When you use the |child| syntax, you can use the |missing| key
+-- with the |child| command to indicate a missing child:
+-- %
+-- \begin{codeexample}[preamble={\usetikzlibrary{graphs,graphdrawing}
+-- \usegdlibrary{trees}}]
+-- \tikz [binary tree layout, level distance=5mm]
+-- \node {a}
+-- child { node {b}
+-- child { node {c}
+-- child { node {d} }
+-- } }
+-- child { node {e}
+-- child [missing]
+-- child { node {f}
+-- child [missing]
+-- child { node {g}
+-- } } };
+-- \end{codeexample}
+-- %
+-- \item When using the |graph| syntax, you can use an ``empty node'',
+-- which really must be completely empty and may not even contain a
+-- slash, to indicate a missing node:
+-- %
+-- \begin{codeexample}[preamble={\usetikzlibrary{graphs,graphdrawing}
+-- \usegdlibrary{trees}}]
+-- \tikz [binary tree layout, level distance=5mm]
+-- \graph { a -> { b -> c -> d, e -> { , f -> { , g} } } };
+-- \end{codeexample}
+-- %
+-- \item You can simply specify the index of a child directly using
+-- the key |desired child index|.
+-- \end{enumerate}
+--
+-- @end
+
+
+-- Imports
+local declare = require("pgf.gd.interface.InterfaceToAlgorithms").declare
+
+
+
+---
+--
+declare {
+ key = "minimum number of children",
+ type = "number",
+ initial = "0",
+
+ summary = [["
+ Specifies how many children a tree node must have at least. If
+ there are less, ``virtual'' children are added.
+ "]],
+ documentation = [["
+ When this key is set to |2| or more, the following happens: We first
+ compute a spanning tree for the graph, see
+ Section~\ref{subsection-gd-spanning-tree}. Then, whenever a node is
+ not a leaf in this spanning tree (when it has at least one child),
+ we add ``virtual'' or ``dummy'' nodes as children of the node until
+ the total number of real and dummy children is at least
+ \meta{number}. If there where at least \meta{number} children at the
+ beginning, nothing happens.
+
+ The new children are added after the existing children. This means
+ that, for instance, in a tree with \meta{number} set to |2|, for
+ every node with a single child, this child will be the first child
+ and the second child will be missing.
+ "]],
+ examples = [["
+ \tikz \graph [binary tree layout,level distance=5mm]
+ { a -> { b->c->d, e->f->g } };
+ "]]
+}
+
+---
+
+declare {
+ key = "desired child index",
+ type = "number",
+
+ summary = [["
+ Pass this key to a node to tell the graph drawing engine which child
+ number you ``desired'' for the node. Whenever all desires for the
+ children of a node are conflict-free, they will all be met; children
+ for which no desired indices were given will remain at their
+ position, whenever possible, but will ``make way'' for children with
+ a desired position.
+ "]],
+ documentation = [["
+ In detail, the following happens: We first
+ determine the total number of children (real or dummy) needed, which
+ is the maximum of the actual number of children, of the
+ \texttt{minimum number of children}, and of the highest desired
+ child index. Then we go over all children that have a desired child
+ index and put they at this position. If the position is already
+ taken (because some other child had the same desired index), the
+ next free position is used with a wrap-around occurring at the
+ end. Next, all children without a desired index are place using the
+ same mechanism, but they want to be placed at the position they had
+ in the original spanning tree.
+
+ While all of this might sound a bit complicated, the application of
+ the key in a binary tree is pretty straightforward: To indicate that
+ a node is a ``right'' child in a tree, just add \texttt{desired child index=2}
+ to it. This will make it a second child, possibly causing the first
+ child to be missing. If there are two nodes specified as children of
+ a node, by saying \texttt{desired child index=}\meta{number} for one
+ of them, you will cause it be first or second child, depending on
+ \meta{number}, and cause the \emph{other} child to become the other
+ child.
+
+ Since |desired child index=2| is a bit long, the following shortcuts
+ are available: |first|, |second|, |third|, and |fourth|.
+ You might wonder why |second| is used rather than |right|. The
+ reason is that trees may also grow left and right and, additionally,
+ the |right| and |left| keys are already in use for
+ anchoring. Naturally, you can locally redefine them, if you want.
+ "]],
+ examples = {[["
+ \tikz \graph [binary tree layout, level distance=5mm]
+ { a -> b[second] };
+ "]],[["
+ \tikz \graph [binary tree layout, level distance=5mm]
+ { a -> { b[second], c} };
+ "]],[["
+ \tikz \graph [binary tree layout, level distance=5mm]
+ { a -> { b, c[first]} };
+ "]],[["
+ \tikz \graph [binary tree layout, level distance=5mm]
+ { a -> { b[second], c[second]} };
+ "]],[["
+ \tikz \graph [binary tree layout, level distance=5mm]
+ { a -> { b[third], c[first], d} };
+ "]]
+ }
+}
+
+
+---
+
+declare {
+ key = "first",
+ use = {
+ { key = "desired child index", value = 1},
+ },
+
+ summary = [["
+ A shorthand for setting the desired child number to |1|.
+ "]]
+ }
+
+---
+
+declare {
+ key = "second",
+ use = {
+ { key = "desired child index", value = 2},
+ },
+
+ summary = [["
+ A shorthand for setting the desired child number to |2|.
+ "]]
+ }
+
+
+---
+
+declare {
+ key = "third",
+ use = {
+ { key = "desired child index", value = 3},
+ },
+
+ summary = [["
+ A shorthand for setting the desired child number to |3|.
+ "]]
+ }
+
+
+---
+
+declare {
+ key = "fourth",
+ use = {
+ { key = "desired child index", value = 4}
+ },
+
+ summary = [["
+ A shorthand for setting the desired child number to |4|.
+ "]]
+ }
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/trees/ReingoldTilford1981.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/trees/ReingoldTilford1981.lua
new file mode 100644
index 0000000000..69babd443c
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/trees/ReingoldTilford1981.lua
@@ -0,0 +1,211 @@
+-- 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$
+
+
+
+---
+-- @section subsubsection {The Reingold--Tilford Layout}
+--
+-- @end
+
+local ReingoldTilford1981 = {}
+
+-- Imports
+local layered = require "pgf.gd.layered"
+local declare = require("pgf.gd.interface.InterfaceToAlgorithms").declare
+local Storage = require "pgf.gd.lib.Storage"
+
+---
+declare {
+ key = "tree layout",
+ algorithm = ReingoldTilford1981,
+
+ preconditions = {
+ connected = true,
+ tree = true
+ },
+
+ postconditions = {
+ upward_oriented = true
+ },
+
+ documentation_in = "pgf.gd.trees.doc"
+}
+
+
+---
+declare {
+ key = "missing nodes get space",
+ type = "boolean",
+ documentation_in = "pgf.gd.trees.doc"
+}
+
+
+
+---
+declare {
+ key = "significant sep",
+ type = "length",
+ initial = "0",
+ documentation_in = "pgf.gd.trees.doc"
+}
+
+
+---
+declare {
+ key = "binary tree layout",
+ use = {
+ { key = "tree layout" },
+ { key = "minimum number of children" , value=2 },
+ { key = "significant sep", value = 10 },
+ },
+ documentation_in = "pgf.gd.trees.doc"
+}
+
+---
+declare {
+ key = "extended binary tree layout",
+ use = {
+ { key = "tree layout" },
+ { key = "minimum number of children" , value=2 },
+ { key = "missing nodes get space" },
+ { key = "significant sep", value = 0 },
+ },
+ documentation_in = "pgf.gd.trees.doc"
+}
+
+
+
+
+-- Now comes the implementation:
+
+function ReingoldTilford1981:run()
+
+ local root = self.spanning_tree.root
+
+ local layers = Storage.new()
+ local descendants = Storage.new()
+
+ self.extended_version = self.digraph.options['missing nodes get space']
+
+ self:precomputeDescendants(root, 1, layers, descendants)
+ self:computeHorizontalPosition(root, layers, descendants)
+ layered.arrange_layers_by_baselines(layers, self.adjusted_bb, self.ugraph)
+
+end
+
+
+function ReingoldTilford1981:precomputeDescendants(node, depth, layers, descendants)
+ local my_descendants = { node }
+
+ for _,arc in ipairs(self.spanning_tree:outgoing(node)) do
+ local head = arc.head
+ self:precomputeDescendants(head, depth+1, layers, descendants)
+ for _,d in ipairs(descendants[head]) do
+ my_descendants[#my_descendants + 1] = d
+ end
+ end
+
+ layers[node] = depth
+ descendants[node] = my_descendants
+end
+
+
+
+function ReingoldTilford1981:computeHorizontalPosition(node, layers, descendants)
+
+ local children = self.spanning_tree:outgoing(node)
+
+ node.pos.x = 0
+
+ local child_depth = layers[node] + 1
+
+ if #children > 0 then
+ -- First, compute positions for all children:
+ for i=1,#children do
+ self:computeHorizontalPosition(children[i].head, layers, descendants)
+ end
+
+ -- Now, compute minimum distances and shift them
+ local right_borders = {}
+
+ for i=1,#children-1 do
+
+ local local_right_borders = {}
+
+ -- Advance "right border" of the subtree rooted at
+ -- the i-th child
+ for _,d in ipairs(descendants[children[i].head]) do
+ local layer = layers[d]
+ local x = d.pos.x
+ if self.extended_version or not (layer > child_depth and d.kind == "dummy") then
+ if not right_borders[layer] or right_borders[layer].pos.x < x then
+ right_borders[layer] = d
+ end
+ if not local_right_borders[layer] or local_right_borders[layer].pos.x < x then
+ local_right_borders[layer] = d
+ end
+ end
+ end
+
+ local left_borders = {}
+ -- Now left for i+1 st child
+ for _,d in ipairs(descendants[children[i+1].head]) do
+ local layer = layers[d]
+ local x = d.pos.x
+ if self.extended_version or not (layer > child_depth and d.kind == "dummy") then
+ if not left_borders[layer] or left_borders[layer].pos.x > x then
+ left_borders[layer] = d
+ end
+ end
+ end
+
+ -- Now walk down the lines and try to find out what the minimum
+ -- distance needs to be.
+
+ local shift = -math.huge
+ local first_dist = left_borders[child_depth].pos.x - local_right_borders[child_depth].pos.x
+ local is_significant = false
+
+ for layer,n2 in pairs(left_borders) do
+ local n1 = right_borders[layer]
+ if n1 then
+ shift = math.max(
+ shift,
+ layered.ideal_sibling_distance(self.adjusted_bb, self.ugraph, n1, n2) + n1.pos.x - n2.pos.x
+ )
+ end
+ if local_right_borders[layer] then
+ if layer > child_depth and
+ (left_borders[layer].pos.x - local_right_borders[layer].pos.x <= first_dist) then
+ is_significant = true
+ end
+ end
+ end
+
+ if is_significant then
+ shift = shift + self.ugraph.options['significant sep']
+ end
+
+ -- Shift all nodes in the subtree by shift:
+ for _,d in ipairs(descendants[children[i+1].head]) do
+ d.pos.x = d.pos.x + shift
+ end
+ end
+
+ -- Finally, position root in the middle:
+ node.pos.x = (children[1].head.pos.x + children[#children].head.pos.x) / 2
+ end
+end
+
+
+
+return ReingoldTilford1981 \ No newline at end of file
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/trees/SpanningTreeComputation.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/trees/SpanningTreeComputation.lua
new file mode 100644
index 0000000000..dacf634865
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/trees/SpanningTreeComputation.lua
@@ -0,0 +1,646 @@
+-- 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$
+
+
+
+---
+-- @section subsection {Spanning Tree Computation}
+--
+-- \label{subsection-gd-spanning-tree}
+-- Although the algorithms of this library are tailored to layout trees,
+-- they will work for any graph as input. First, if the graph is not
+-- connected, it is decomposed into connected components and these are
+-- laid out individually. Second, for each component, a spanning tree of
+-- the graph is computed first and the layout is computed for this
+-- spanning tree; all other edges will still be drawn, but they have no
+-- impact on the placement of the nodes. If the graph is already a tree,
+-- the spanning tree will be the original graph.
+--
+-- The computation of the spanning tree is a non-trivial process since
+-- a non-tree graph has many different possible spanning trees. You can
+-- choose between different methods for deciding on a spanning tree, it
+-- is even possible to implement new algorithms. (In the future, the
+-- computation of spanning trees and the cycle removal in layered graph
+-- drawing algorithms will be unified, but, currently, they are
+-- implemented differently.)
+--
+-- Selects the (sub)algorithm that is to be used for computing spanning
+-- trees whenever this is requested by a tree layout algorithm. The
+-- default algorithm is |breadth first spanning tree|.
+--%
+--\begin{codeexample}[preamble={\usetikzlibrary{graphs,graphdrawing}
+-- \usegdlibrary{trees}}]
+--\tikz \graph [tree layout, breadth first spanning tree]
+--{
+-- 1 -- {2,3,4,5} -- 6;
+--};
+--\end{codeexample}
+--%
+--\begin{codeexample}[preamble={\usetikzlibrary{graphs,graphdrawing}
+-- \usegdlibrary{trees}}]
+--\tikz \graph [tree layout, depth first spanning tree]
+--{
+-- 1 --[bend right] {2,3,4,5 [>bend left]} -- 6;
+--};
+--\end{codeexample}
+--
+-- @end
+
+local SpanningTreeComputation = {}
+
+
+
+-- Namespace
+require("pgf.gd.trees").SpanningTreeComputation = SpanningTreeComputation
+
+
+-- Imports
+local lib = require "pgf.gd.lib"
+
+local Vertex = require "pgf.gd.model.Vertex"
+local Digraph = require "pgf.gd.model.Digraph"
+
+
+local declare = require("pgf.gd.interface.InterfaceToAlgorithms").declare
+
+
+
+
+-- ------------------------- --
+-- General tree parameters --
+-- ------------------------- --
+
+
+
+
+---
+--
+declare {
+ key = "breadth first spanning tree",
+ algorithm = {
+ run =
+ function (self)
+ return SpanningTreeComputation.computeSpanningTree(self.ugraph, false, self.events)
+ end
+ },
+ phase = "spanning tree computation",
+ phase_default = true,
+
+ summary = [["
+ This key selects ``breadth first'' as the (sub)algorithm for
+ computing spanning trees. Note that this key does not cause a graph
+ drawing scope to start; the key only has an effect in conjunction
+ with keys like |tree layout|.
+"]],
+ documentation = [["
+ The algorithm will be called whenever a graph drawing algorithm
+ needs a spanning tree on which to operate. It works as follows:
+ %
+ \begin{enumerate}
+ \item It looks for a node for which the |root| parameter is
+ set. If there are several such nodes, the first one is used.
+ If there are no such nodes, the first node is used.
+
+ Let call the node determined in this way the \emph{root node}.
+ \item For every edge, a \emph{priority} is determined, which is a
+ number between 1 and 10. How this happens, exactly, will be
+ explained in a moment. Priority 1 means ``most important'' while
+ priority 10 means ``least important''.
+ \item Starting from the root node, we now perform a breadth first
+ search through the tree, thereby implicitly building a spanning
+ tree: Suppose for a moment that all edges have priority~1. Then,
+ the algorithm works just the way that a normal breadth first
+ search is performed: We keep a queue of to-be-visited nodes and
+ while this queue is not empty, we remove its first node. If this
+ node has not yet been visited, we add all its neighbors at the
+ end of the queue. When a node is taken out of the queue, we make
+ it the child of the node whose neighbor it was when it was
+ added. Since the queue follows the ``first in, first out''
+ principle (it is a fifo queue), the children of the root will be
+ all nodes at distance $1$ form the root, their children will be
+ all nodes at distance $2$, and so on.
+ \item Now suppose that some edges have a priority different
+ from~1, in which case things get more complicated. We now keep
+ track of one fifo queue for each of the ten possible
+ priorities. When we consider the neighbors of a node, we actually
+ consider all its incident edges. Each of these edges has a certain
+ priority and the neighbor is put into the queue of the edge's
+ priority. Now, we still remove nodes normally from the queue for
+ priority~1; only if this queue is empty and there is still a node
+ in the queue for priority~2 we remove the first element from this
+ queue (and proceed as before). If the second queue is also empty,
+ we try the third, and so on up to the tenth queue. If all queues
+ are empty, the algorithm stops.
+ \end{enumerate}
+
+ The effect of the ten queues is the following: If the edges of
+ priority $1$ span the whole graph, a spanning tree consisting solely
+ of these edges will be computed. However, if they do not, once we
+ have visited reachable using only priority 1 edges, we will extend
+ the spanning tree using a priority 2 edge; but then we once switch
+ back to using only priority 1 edges. If neither priority~1 nor
+ priority~2 edges suffice to cover the whole graph, priority~3 edges
+ are used, and so on.
+ "]]
+}
+
+---
+
+declare {
+ key = "depth first spanning tree",
+ algorithm = {
+ run =
+ function (self)
+ return SpanningTreeComputation.computeSpanningTree(self.ugraph, true, self.events)
+ end
+ },
+ phase = "spanning tree computation",
+
+ summary = [["
+ Works exactly like |breadth first spanning tree| (same handling of
+ priorities), only the queues are now lifo instead of
+ fifo.
+ "]]
+}
+
+---
+--
+declare {
+ key = "root",
+ type = "boolean",
+ default = true,
+
+ summary = [["
+ This Boolean parameter is used in the computation of spanning
+ trees. When can be set for a node, this node will be used as the
+ root for the spanning tree computation. If several nodes have this
+ option set, the first node will be used.
+ "]]
+}
+
+
+---
+--
+declare {
+ key = "span priority",
+ type = "number",
+
+ summary = [["
+ Explicitly sets the ``span priority'' of an edge to \meta{number}, which must be
+ a number between |1| and |10|. The priority of edges is used by
+ spanning tree computations, see |breadth first spanning tree|.
+ "]]
+}
+
+
+
+---
+-- when it comes to choosing which edges are part of the spanning tree.
+declare {
+ key = "span edge",
+ use = {
+ { key = "span priority", value = 1 },
+ },
+
+ summary = [["
+ An easy-to-remember shorthand for |span priority=1|. When this key
+ is used with an edge, it will always be preferred over other edges
+ "]]
+}
+
+
+
+
+---
+--
+declare {
+ key = "no span edge",
+ use = {
+ { key = "span priority", value = 10 },
+ },
+
+ summary = [["
+ An easy-to-remember shorthand for |span priority=10|. This causes
+ the edge to be used only as a last resort as part of a spanning
+ tree.
+ "]],
+ documentation = [["
+ In the example, we add lots of edges that would normally be
+ preferred in the computation of the spanning tree, but use
+ |no span edge| to cause the algorithm to ignore these edges.
+ "]],
+ examples = [["
+ \tikz \graph [tree layout, nodes={draw}, sibling distance=0pt,
+ every group/.style={
+ default edge kind=->, no span edge,
+ path=source}]
+ {
+ 5 -> {
+ "1,3" -> {0,2,4},
+ 11 -> {
+ "7,9" -> { 6, 8, 10 }
+ }
+ }
+ };
+ "]]
+}
+
+
+
+---
+declare {
+ key = "span priority ->",
+ type = "number",
+ initial = "3",
+
+ summary = [["
+ This key stores the span priority of all edges whose direction is
+ |->|. There are similar keys for all other directions, such as
+ |span priority <-| and so on.
+ "]],
+ documentation = [["
+ When you write
+ %
+\begin{codeexample}[code only]
+graph { a -> b -- c <- [span priority=2] d }
+\end{codeexample}
+ %
+ the priority of the edge from |a| to |b| would be the current
+ value of the key |span priority ->|, the priority of the edge from
+ |b| to |c| would be the current value of |span priority --|, and
+ the priority of the edge from |c| to |d| would be |2|, regardless
+ of the value of |span priority <-|.
+
+ The defaults for the priorities are:
+ %
+ \begin{itemize}
+ \item |span priority -> = 3|
+ \item |span priority -- = 5|
+ \item |span priority <-> = 5|
+ \item |span priority <- = 8|
+ \item |span priority -!- = 10|
+ \end{itemize}
+ "]]
+}
+
+
+
+---
+
+declare {
+ key = "span priority reversed ->",
+ type = "number",
+ initial = "9",
+
+ documentation = [["
+ This key stores the span priority of traveling across reversed
+ edges whose actual direction is |->| (again, there are similar keys
+ for all other directions).
+ "]],
+ documentation = [["
+ When you write
+ %
+\begin{codeexample}[code only]
+graph { a -> b -- c <- [span priority=2] d }
+\end{codeexample}
+ %
+ there are, in addition to the priorities indicated above, also
+ further edge priorities: The priority of the (reversed) edge |b|
+ to |a| is |span priority reversed ->|, the priority of the
+ (reversed) edge |c| to |b| is |span priority reversed --|, and the
+ span priority of the reversed edge |d| to |c| is |2|, regardless
+ of the value of |span priority reversed <-|.
+
+ The defaults for the priorities are:
+ %
+ \begin{itemize}
+ \item |span priority reversed -> = 9|
+ \item |span priority reversed -- = 5|
+ \item |span priority reversed <-> = 5|
+ \item |span priority reversed <- = 7|
+ \item |span priority reversed -!- = 10|
+ \end{itemize}
+
+ The default priorities are set in such a way, that non-reversed |->|
+ edges have top priorities, |--| and |<->| edges have the same
+ priorities in either direction, and |<-| edges have low priority in
+ either direction (but going |a <- b| from |b| to |a| is given higher
+ priority than going from |a| to |b| via this edge and also higher
+ priority than going from |b| to |a| in |a -> b|).
+
+ Keys like |span using directed| change the priorities ``en bloc''.
+ "]]
+}
+
+
+declare {
+ key = "span priority <-",
+ type = "number",
+ initial = "8",
+}
+
+declare {
+ key = "span priority reversed <-",
+ type = "number",
+ initial = "7",
+}
+
+declare {
+ key = "span priority --",
+ type = "number",
+ initial = "5",
+}
+
+declare {
+ key = "span priority reversed --",
+ type = "number",
+ initial = "5",
+}
+
+declare {
+ key = "span priority <->",
+ type = "number",
+ initial = "5",
+}
+
+declare {
+ key = "span priority reversed <->",
+ type = "number",
+ initial = "5",
+}
+
+declare {
+ key = "span priority -!-",
+ type = "number",
+ initial= "10",
+}
+
+declare {
+ key = "span priority reversed -!-",
+ type = "number",
+ initial= "10",
+}
+
+---
+
+declare {
+ key = "span using directed",
+ use = {
+ { key = "span priority reversed <-", value = 3},
+ { key = "span priority <->", value = 3},
+ { key = "span priority reversed <->", value = 3},
+ },
+ summary = [["
+ This style sets a priority of |3| for all edges that are directed
+ and ``go along the arrow direction'', that is, we go from |a| to
+ |b| with a priority of |3| for the cases |a -> b|, |b <- a|,
+ |a <-> b|, and |b <-> a|.
+ This strategy is nice with trees specified with both forward and
+ backward edges.
+ "]],
+ examples = [["
+ \tikz \graph [tree layout, nodes={draw}, sibling distance=0pt,
+ span using directed]
+ {
+ 3 <- 5[root] -> 8,
+ 1 <- 3 -> 4,
+ 7 <- 8 -> 9,
+ 1 -- 4 -- 7 -- 9
+ };
+ "]]
+}
+
+---
+
+declare {
+ key = "span using all",
+ use = {
+ { key = "span priority <-", value = 5},
+ { key = "span priority ->", value = 5},
+ { key = "span priority <->", value = 5},
+ { key = "span priority --", value = 5},
+ { key = "span priority -!-", value = 5},
+ { key = "span priority reversed <-", value = 5},
+ { key = "span priority reversed ->", value = 5},
+ { key = "span priority reversed <->", value = 5},
+ { key = "span priority reversed --", value = 5},
+ { key = "span priority reversed -!-", value = 5},
+ },
+
+ summary = [["
+ Assings a uniform priority of 5 to all edges.
+ "]]
+}
+
+
+-- The implementation
+
+--
+-- Compute a spanning tree of a graph
+--
+-- The algorithm will favor nodes according to their priority. This is
+-- determined through an edge priority function.
+--
+-- @param ugraph An undirected graph for which the spanning tree
+-- should be computed
+-- @param dfs True if depth first should be used, false if breadth
+-- first should be used.
+--
+-- @return A new graph that is a spanning tree.
+
+function SpanningTreeComputation.computeSpanningTree (ugraph, dfs, events)
+
+ local tree = Digraph.new (ugraph) -- copy vertices
+
+ local edge_priorities = ugraph.options['/graph drawing/edge priorities']
+
+ local root = lib.find(ugraph.vertices, function (v) return v.options['root'] end) or ugraph.vertices[1]
+
+ -- Traverse tree, giving preference to directed edges and, that
+ -- failing, to undirected and bidirected edges, and, that failing,
+ -- all other edges.
+ local marked = {}
+
+ local stacks = { -- 10 stacks for 10 priorities, with 1 being the highest
+ { { parent = nil, node = root}, top = 1, bottom = 1 },
+ { top = 0, bottom = 1},
+ { top = 0, bottom = 1},
+ { top = 0, bottom = 1},
+ { top = 0, bottom = 1},
+ { top = 0, bottom = 1},
+ { top = 0, bottom = 1},
+ { top = 0, bottom = 1},
+ { top = 0, bottom = 1},
+ { top = 0, bottom = 1}
+ }
+
+ local function stack_is_non_empty (s) return s.top >= s.bottom end
+
+ while lib.find(stacks, stack_is_non_empty) do
+ local parent, node
+
+ for _,stack in ipairs(stacks) do
+ if stack_is_non_empty(stack) then
+ -- Pop
+ parent = stack[stack.top].parent
+ node = stack[stack.top].node
+
+ stack[stack.top] = nil
+ stack.top = stack.top - 1
+
+ break
+ end
+ end
+
+ if not marked[node] then
+
+ -- The node is good!
+ marked[node] = true
+
+ if parent then
+ tree:connect(parent,node)
+ end
+
+ local arcs = ugraph:outgoing(node)
+
+ for j=1,#arcs do
+ local arc = arcs[dfs and j or #arcs - j + 1]
+ local head = arc.head
+
+ if not marked[head] then
+ local priority = arc:spanPriority()
+ local stack = assert(stacks[priority], "illegal edge priority")
+ if dfs then
+ stack.top = stack.top + 1
+ stack[stack.top] = { parent = node, node = head}
+ else
+ stack.bottom = stack.bottom - 1
+ stack[stack.bottom] = { parent = node, node = head}
+ end
+ end
+ end
+ end
+ end
+
+ -- Now, copy vertex list
+ local copy = {}
+ for i,v in ipairs(tree.vertices) do
+ copy[i] = v
+ end
+
+ -- Now, setup child lists
+ for _,v in ipairs(copy) do
+
+ -- Children as they come from the spanning tree computation
+ tree:sortOutgoing(v, function (a,b) return a:eventIndex() < b:eventIndex() end)
+ local outgoings = tree:outgoing(v)
+
+ -- Compute children as they come in the event list:
+ local children = {}
+
+ local i = (v.event.index or 0)+1
+ while i <= #events and events[i].kind == "edge" do
+ i = i + 1
+ end
+
+ if events[i] and events[i].kind == "begin" and events[i].parameters == "descendants" then
+ -- Ok, the node is followed by a descendants group
+ -- Now scan for nodes that are not inside a descendants group
+ local stop = events[i].end_index
+ local j = i+1
+ while j <= stop do
+ if events[j].kind == "node" then
+ children[#children+1] = events[j].parameters
+ elseif events[j].kind == "begin" and events[j].parameters == "descendants" then
+ j = events[j].end_index
+ end
+ j = j + 1
+ end
+
+ -- Test, whether outgoings and children contain the same nodes:
+ local function same_elements()
+ local hash = {}
+ for v,c in ipairs(outgoings) do
+ hash[c.head] = true
+ end
+ local count = 0
+ for _,c in pairs(children) do
+ if c ~= "" then
+ count = count + 1
+ if not hash[c] or count > #outgoings then
+ return false
+ end
+ end
+ end
+ return count == #outgoings
+ end
+
+ if same_elements() and #outgoings > 0 then
+ -- increase number of children, if necessary
+ local needed = math.max(#children, lib.lookup_option('minimum number of children', v, ugraph))
+ for i=1,#children do
+ if children[i] ~= "" then
+ local d = children[i].options['desired child index']
+ needed = d and math.max(needed, d) or needed
+ end
+ end
+
+ local new_children = {}
+ for i=1,#children do
+ if children[i] ~= "" then
+ local d = children[i].options['desired child index']
+ if d then
+ local target = d
+
+ while new_children[target] do
+ target = 1 + (target % #children)
+ end
+ new_children[target] = children[i]
+ end
+ end
+ end
+ for i=1,#children do
+ if children[i] ~= "" then
+ local d = children[i].options['desired child index']
+ if not d then
+ local target = i
+
+ while new_children[target] do
+ target = 1 + (target % #children)
+ end
+ new_children[target] = children[i]
+ end
+ end
+ end
+ for i=1,needed do
+ if not new_children[i] then
+ local new_child = Vertex.new{ kind = "dummy" }
+ new_children[i] = new_child
+ tree:add {new_child}
+ tree:connect(v,new_child)
+ end
+ end
+
+ tree:orderOutgoing(v,new_children)
+ end
+ end
+ end
+
+ tree.root = root
+
+ return tree
+end
+
+
+
+-- Done
+
+return SpanningTreeComputation
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/trees/doc.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/trees/doc.lua
new file mode 100644
index 0000000000..a44fd47031
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/trees/doc.lua
@@ -0,0 +1,340 @@
+-- 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$
+
+
+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 "tree layout"
+
+summary "This layout uses the Reingold--Tilform method for drawing trees."
+
+documentation
+[[
+The Reingold--Tilford method is a standard method for drawing
+trees. It is described in:
+%
+\begin{itemize}
+ \item
+ E.~M.\ Reingold and J.~S.\ Tilford,
+ \newblock Tidier drawings of trees,
+ \newblock \emph{IEEE Transactions on Software Engineering,}
+ 7(2), 223--228, 1981.
+\end{itemize}
+%
+My implementation in |graphdrawing.trees| follows the following paper, which
+introduces some nice extensions of the basic algorithm:
+%
+\begin{itemize}
+ \item
+ A.\ Br\"uggemann-Klein, D.\ Wood,
+ \newblock Drawing trees nicely with \TeX,
+ \emph{Electronic Publishing,} 2(2), 101--115, 1989.
+\end{itemize}
+%
+As a historical remark, Br\"uggemann-Klein and Wood have implemented
+their version of the Reingold--Tilford algorithm directly in \TeX\
+(resulting in the Tree\TeX\ style). With the power of Lua\TeX\ at
+our disposal, the 2012 implementation in the |graphdrawing.tree|
+library is somewhat more powerful and cleaner, but it really was an
+impressive achievement to implement this algorithm back in 1989
+directly in \TeX.
+
+The basic idea of the Reingold--Tilford algorithm is to use the
+following rules to position the nodes of a tree (the following
+description assumes that the tree grows downwards, other growth
+directions are handled by the automatic orientation mechanisms of
+the graph drawing library):
+%
+\begin{enumerate}
+ \item For a node, recursively compute a layout for each of its children.
+ \item Place the tree rooted at the first child somewhere on the page.
+ \item Place the tree rooted at the second child to the right of the
+ first one as near as possible so that no two nodes touch (and such
+ that the |sibling sep| padding is not violated).
+ \item Repeat for all subsequent children.
+ \item Then place the root above the child trees at the middle
+ position, that is, at the half-way point between the left-most and
+ the right-most child of the node.
+\end{enumerate}
+%
+The standard keys |level distance|, |level sep|, |sibling distance|,
+and |sibling sep|, as well as the |pre| and |post| versions of these
+keys, as taken into consideration when nodes are positioned. See also
+Section~\ref{subsection-gd-dist-pad} for details on these keys.
+
+\noindent\textbf{Handling of Missing Children.}
+As described in Section~\ref{section-gd-missing-children}, you can
+specify that some child nodes are ``missing'' in the tree, but some
+space should be reserved for them. This is exactly what happens:
+When the subtrees of the children of a node are arranged, each
+position with a missing child is treated as if a zero-width,
+zero-height subtree were present at that positions:
+%
+\begin{codeexample}[preamble={\usetikzlibrary{graphs,graphdrawing}
+ \usegdlibrary{trees}}]
+\tikz [tree layout, nodes={draw,circle}]
+ \node {r}
+ child { node {a}
+ child [missing]
+ child { node {b} }
+ }
+ child[missing];
+\end{codeexample}
+%
+or in |graph| syntax:
+%
+\begin{codeexample}[preamble={\usetikzlibrary{graphs,graphdrawing}
+ \usegdlibrary{trees}}]
+ \tikz \graph [tree layout, nodes={draw,circle}]
+ {
+ r -> {
+ a -> {
+ , %missing
+ b},
+ % missing
+ }
+ };
+\end{codeexample}
+%
+More than one child can go missing:
+%
+\begin{codeexample}[preamble={\usetikzlibrary{graphs,graphdrawing}
+ \usegdlibrary{trees}}]
+\tikz \graph [tree layout, nodes={draw,circle}, sibling sep=0pt]
+ { r -> { a, , ,b -> {c,d}, ,e} };
+\end{codeexample}
+%
+Although missing children are taken into consideration for the
+computation of the placement of the children of a root node relative
+to one another and also for the computation of the position of the
+root node, they are usually \emph{not} considered as part of the
+``outline'' of a subtree (the \texttt{minimum number of children}
+key ensures that |b|, |c|, |e|, and |f| all have a missing right
+child):
+%
+\begin{codeexample}[preamble={\usetikzlibrary{graphs,graphdrawing}
+ \usegdlibrary{trees}}]
+\tikz \graph [tree layout, minimum number of children=2,
+ nodes={draw,circle}]
+ { a -> { b -> c -> d, e -> f -> g } };
+\end{codeexample}
+%
+This behaviour of ``ignoring'' missing children in later stages of
+the recursion can be changed using the key |missing nodes get space|.
+
+\noindent\textbf{Significant Pairs of Siblings.}
+Br\"uggemann-Klein and Wood have proposed an extension of the
+Reingold--Tilford method that is intended to better highlight the
+overall structure of a tree. Consider the following two trees:
+%
+\begin{codeexample}[preamble={\usetikzlibrary{graphs,graphdrawing}
+ \usegdlibrary{trees}}]
+\tikz [baseline=(a.base), tree layout, minimum number of children=2,
+ sibling distance=5mm, level distance=5mm]
+ \graph [nodes={circle, inner sep=0pt, minimum size=2mm, fill, as=}]{
+ a -- { b -- c -- { d -- e, f -- { g, h }}, i -- j -- k[second] }
+ };\quad
+\tikz [baseline=(a.base), tree layout, minimum number of children=2,
+ sibling distance=5mm, level distance=5mm]
+ \graph [nodes={circle, inner sep=0pt, minimum size=2mm, fill, as=}]{
+ a -- { b -- c -- d -- e, i -- j -- { f -- {g,h}, k } }
+ };
+\end{codeexample}
+%
+As observed by Br\"uggemann-Klein and Wood, the two trees are
+structurally quite different, but the Reingold--Tilford method
+places the nodes at exactly the same positions and only one edge
+``switches'' positions. In order to better highlight the differences
+between the trees, they propose to add a little extra separation
+between siblings that form a \emph{significant pair}. They define
+such a pair as follows: Consider the subtrees of two adjacent
+siblings. There will be one or more levels where these subtrees have
+a minimum distance. For instance, the following two trees the
+subtrees of the nodes |a| and |b| have a minimum distance only at
+the top level in the left example, and in all levels in the second
+example. A \emph{significant pair} is a pair of siblings where the
+minimum distance is encountered on any level other than the first
+level. Thus, in the first example there is no significant pair,
+while in the second example |a| and |b| form such a pair.
+%
+\begin{codeexample}[preamble={\usetikzlibrary{graphs,graphdrawing}
+ \usegdlibrary{trees}}]
+\tikz \graph [tree layout, minimum number of children=2,
+ level distance=5mm, nodes={circle,draw}]
+ { / -> { a -> / -> /, b -> /[second] -> /[second] }};
+ \quad
+\tikz \graph [tree layout, minimum number of children=2,
+ level distance=5mm, nodes={circle,draw}]
+ { / -> { a -> / -> /, b -> / -> / }};
+\end{codeexample}
+%
+Whenever the algorithm encounters a significant pair, it adds extra
+space between the siblings as specified by the |significant sep|
+key.
+]]
+
+
+example
+[[
+\tikz [tree layout, sibling distance=8mm]
+ \graph [nodes={circle, draw, inner sep=1.5pt}]{
+ 1 -- { 2 -- 3 -- { 4 -- 5, 6 -- { 7, 8, 9 }}, 10 -- 11 -- { 12, 13 } }
+ };
+]]
+
+
+example
+[[
+\tikz [tree layout, grow=-30,
+ sibling distance=0mm, level distance=0mm,]
+ \graph [nodes={circle, draw, inner sep=1.5pt}]{
+ 1 -- { 2 -- 3 -- { 4 -- 5, 6 -- { 7, 8, 9 }}, 10 -- 11 -- { 12, 13 } }
+ };
+]]
+--------------------------------------------------------------------
+
+
+
+--------------------------------------------------------------------
+key "missing nodes get space"
+
+summary
+[[
+When set to true, missing children are treated as if they where
+zero-width, zero-height nodes during the whole tree layout process.
+]]
+
+
+example
+[[
+\tikz \graph [tree layout, missing nodes get space,
+ minimum number of children=2, nodes={draw,circle}]
+{ a -> { b -> c -> d, e -> f -> g } };
+]]
+--------------------------------------------------------------------
+
+
+
+
+
+--------------------------------------------------------------------
+key "significant sep"
+
+summary
+[[
+This space is added to significant pairs by the modified
+Reingold--Tilford algorithm.
+]]
+
+example
+[[
+\tikz [baseline=(a.base), tree layout, significant sep=1em,
+ minimum number of children=2,
+ sibling distance=5mm, level distance=5mm]
+ \graph [nodes={circle, inner sep=0pt, minimum size=2mm, fill, as=}]{
+ a -- { b -- c -- { d -- e, f -- { g, h }}, i -- j -- k[second] }
+ };\quad
+\tikz [baseline=(a.base), tree layout, significant sep=1em,
+ minimum number of children=2,
+ sibling distance=5mm, level distance=5mm]
+ \graph [nodes={circle, inner sep=0pt, minimum size=2mm, fill, as=}]{
+ a -- { b -- c -- d -- e, i -- j -- { f -- {g,h}, k } }
+ };
+]]
+--------------------------------------------------------------------
+
+
+
+--------------------------------------------------------------------
+key "binary tree layout"
+
+summary
+[[
+A layout based on the Reingold--Tilford method for drawing
+binary trees.
+]]
+
+documentation
+[[
+This key executes:
+%
+\begin{enumerate}
+ \item |tree layout|, thereby selecting the Reingold--Tilford method,
+ \item |minimum number of children=2|, thereby ensuring the all nodes
+ have (at least) two children or none at all, and
+ \item |significant sep=10pt| to highlight significant pairs.
+\end{enumerate}
+%
+In the examples, the last one is taken from the paper of
+Br\"uggemann-Klein and Wood. It demonstrates nicely the
+advantages of having the full power of \tikzname's anchoring and the
+graph drawing engine's orientation mechanisms at one's disposal.
+]]
+
+
+example
+[[
+\tikz [grow'=up, binary tree layout, sibling distance=7mm, level distance=7mm]
+ \graph {
+ a -- { b -- c -- { d -- e, f -- { g, h }}, i -- j -- k[second] }
+ };
+]]
+
+--[[
+% TODOsp: codeexamples: the next example needs the library `arrows.meta`
+--]]
+example
+[[
+\tikz \graph [binary tree layout] {
+ Knuth -> {
+ Beeton -> Kellermann [second] -> Carnes,
+ Tobin -> Plass -> { Lamport, Spivak }
+ }
+};\qquad
+\tikz [>={Stealth[round,sep]}]
+ \graph [binary tree layout, grow'=right, level sep=1.5em,
+ nodes={right, fill=blue!50, text=white, chamfered rectangle},
+ edges={decorate,decoration={snake, post length=5pt}}]
+ {
+ Knuth -> {
+ Beeton -> Kellermann [second] -> Carnes,
+ Tobin -> Plass -> { Lamport, Spivak }
+ }
+ };
+]]
+--------------------------------------------------------------------
+
+
+
+--------------------------------------------------------------------
+key "extended binary tree layout"
+
+summary
+[[
+This algorithm is similar to |binary tree layout|, only the
+option \texttt{missing nodes get space} is executed and the
+\texttt{significant sep} is zero.
+]]
+
+example
+[[
+\tikz [grow'=up, extended binary tree layout,
+ sibling distance=7mm, level distance=7mm]
+ \graph {
+ a -- { b -- c -- { d -- e, f -- { g, h }}, i -- j -- k[second] }
+ };
+]]
+--------------------------------------------------------------------
diff --git a/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/trees/library.lua b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/trees/library.lua
new file mode 100644
index 0000000000..a0019666ac
--- /dev/null
+++ b/graphics/pgf/base/tex/generic/graphdrawing/lua/pgf/gd/trees/library.lua
@@ -0,0 +1,35 @@
+-- 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$
+
+
+
+---
+-- \tikzname\ offers several different syntax to specify trees (see
+-- Sections \ref{section-library-graphs}
+-- and~\ref{section-trees}). The job of the graph drawing algorithms from
+-- this library is to turn the specification of trees into beautiful
+-- layouts.
+--
+-- We start this section with a description of algorithms, then we have a
+-- look at how missing children can be specified and at what happens when
+-- the input graph is not a tree.
+--
+-- @library
+
+local trees -- Library name
+
+-- Load declarations from:
+require "pgf.gd.trees.ChildSpec"
+require "pgf.gd.trees.SpanningTreeComputation"
+
+-- Load algorithms from:
+require "pgf.gd.trees.ReingoldTilford1981"
+