The TikZ and PGF Packages
Manual for version 3.1.10
39 The Binding Layer¶
This section explains how the binding of the graph drawing system to a particular display layer works. Let me stress that all of this is important only for readers who
• either wish to write new display system (see Section 38)
• or wish to know more about how the graph drawing system works on the pure pgf layer (this is were the binding occurs).
Bindings are used to encapsulate the details of the communication between the graph drawing system and a display system (see Section 38 for an introduction to display systems).
Consider a display system that communicates with the graph drawing system. At some point, the display system would like to run an algorithm to lay out a graph. To achieve this, it will call different functions from the class InterfaceToDisplay and the effect of this is that a representation of the to-be-drawn graph is constructed internally and that the appropriate algorithms are run. All of this is in some sense independent of the actual display system, the class InterfaceToDisplay offers the same standard interface to all display systems.
At some point, however, the graph drawing system may need to “talk back” to the display system. For instance, once the graph has been laid out, to trigger the actual rendering of the graph, the graph drawing system must “tell” the display layer where the vertices lie. For some display systems this is easy: if the display system itself is written in Lua, it could just access the syntactic digraph directly. However, for systems like TikZ or systems written in another language, the graph drawing system needs a set of functions that it can call that will tell the display system what is going on. This is were bindings come in.
The class Binding is an interface that defines numerous methods that will be called by the graph drawing system in different situations (see the documentation below for details). For instance, there is a function renderVertex that is called by the graph drawing system whenever a vertex should be rendered by the display system. The class is really just an interface in the sense of object-oriented programming. For each display system you need to create a subclass of Binding like BindingToPGF or BindingToASCII that implement the methods declared by Binding. The number of methods that need to be implemented depends on the display system.
In the following, you will find the documentation of the Binding class in Section 39.2. Following this, we first have a quick look at how the BindingToPGF works and then go over a simple example of a binding to a more or less imaginary display system. This example should help readers interested in implementing their own bindings.
39.2 The Binding Class and the Interface Core¶
Lua table Binding (declared in pgf.gd.bindings.Binding)
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.
A Storage storing the information passed from the display layer. The interpretation of this left to the actual binding.
Alphabetical method summary:
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.
Parameters: 1. t See InterfaceToAlgorithms.declare for details.
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:renderCollectionStartKind(kind, layer)
This function and the corresponding ...Stop... functions are called whenever a collection kind should be rendered. See InterfaceToDisplay.render_collections for details.
Parameters: 1. kind The kind (a string). 2. layer The kind’s layer (a number).
function Binding:renderCollectionStopKind(kind, layer)
The counterpart to renderCollectionStartKind.
Parameters: 1. kind The kind. 2. layer The kind’s layer.
Renders a single collection, see renderCollectionStartKind for details.
Parameters: 1. collection The collection object.
This function and the corresponding ...Stop... functions are called whenever a vertex should be rendered. See InterfaceToDisplay.render_vertices for details.
The counterpart to renderVerticesStop.
Renders a single vertex, see renderVertexStartKind for details.
Parameters: 1. vertex The Vertex object.
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.
Parameters: 1. v The vertex.
This function and the corresponding ...Stop... functions are called whenever an edge should be rendered. See InterfaceToDisplay.render_edges for details.
The counterpart to renderEdgesStop.
Renders a single vertex, see renderEdgeStartKind for details.
Parameters: 1. edge The Edge object.
Like everyVertexCreation, only for edges.
Parameters: 1. e The edge.
Generate a new vertex. This method will be called when the 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.
Parameters: 1. init A table of initial values for the node. The following fields will be used:
• 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. • shape If present, a shape of the node. • generated_options A table that is passed back to the display layer as a list of key–value pairs. • 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.
Lua table InterfaceCore (declared in pgf.gd.interface.InterfaceCore)
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.
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.
This is a stack of graph drawing scopes. All interface methods refer to the top of this stack.
This table stores which collection kinds have been defined together with their properties.
A table that maps algorithm keys (like tree layout to class objects).
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.
Alphabetical method summary:
Returns the top scope
Returns: 1. The current top scope, which is the scope in which everything should happen right now.
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.
Parameters: 1. s A parameter value or a string. 2. t The type of the parameter
Returns: 1. If s is not a string, it is just returned. If it is a string, it is converted to the type t.
39.3 The Binding To PGF¶
Lua table BindingToPGF (declared in pgf.gd.bindings.BindingToPGF)
This class, which is a subclass of Binding, binds the graph drawing system to the pgf display system by overriding (that is, implementing) the methods of the Binding class. As a typical example, consider the implementation of the function renderVertex:
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 pgf 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).
39.4 An Example Binding Class¶
In the present section a complete binding is presented to an imaginary “ascii art display system” is presented. The idea is that this display system will depict graphs using just normal letters and spaces so that, when the text is typeset in a monospace font, a visualization of the graph results. For instance:
Graph rendered by BindingToASCII:
Alice ....... .. . . . ... . . . ... .. . .. .. . . . Charly Bob . . .. . . . . . . . . . . . .. . . . .. . . Dave George . .. . ... . . . .. . . . ... .. . . ... .. . .. Eve . .. .. . .. . . . . . . .. . .. ... Fritz
The binding will reside in a file BindingToASCII.lua, whose contents is detailed below, and which is used by calling the bind function of InterfaceToDisplay, see its documentation for details.
The binding’s code starts with some initializations:
The interesting code is the code for “rendering” a graph. The graph drawing system will invoke the binding’s methods renderStart and renderStop to signal that the graph drawing algorithms have finished and that the vertices and edges can now be drawn.
In our ascii renderer, we use a two-dimensional field holding characters that severs as the “drawing canvas”. At the beginning of the rendering, we initialize it with blanks:
In order to “render” a vertex, the graph drawing system will call the renderVertex method. The binding of TikZ does a lot of complicated things in this method to retrieve the underlying node’s box from internal table and to somehow reinstall the box in TeX’s output stream; for our ascii binding things are much simpler: We simply put the vertex’s name at the canvas position corresponding to the vertex’s pos coordinate. Note that this simple version of an ascii renderer does not try to scale things; thus, array out of bounds might occur here.
The rendering of edges is a more complicated process. Given two vertices, we put dots at the canvas positions between them; provided there are no vertices (so edges are behind the nodes). Here is the essential part of the code (for the complete code, have a look at pgf/gd/examples/BindingToASCII.lua):
The methods renderVertex and renderEdge will be called once for each vertex and edge of the to-be-rendered graph. At the end, the renderStop method is called. In our case, this method will output the canvas using print. A slight complication arises when node names are longer than just one character. In this case, the following code “centers” them on their coordinate and makes sure that they do not get overwritten by the dots forming edges:
At the end, we need to return the created object: