One of the main tasks of class GT_Graph is to provide
attributes for graphs, nodes and edges. Generally, there are two classes of
attributes: predefined attributes and user
definded attributes. Predefined attributes are those
Graphlet knows of, for example node and edge identifier
numbers, or coordinates. In contrast, user defined attributes
are arbitrary attributes which are added by an algorithm or user supplied
extension.
Both predefined and user defined are accessed with the gt methods:
GT_Graph_Attributes& GT_Graph::gt ()const GT_Node_Attributes& GT_Graph::gt () const
GT_Node_Attributes& GT_Graph::gt (node n)const GT_Node_Attributes& GT_Graph::gt (const node n) const
GT_Edge_Attributes& GT_Graph::gt (edge e)const GT_Edge_Attributes& GT_Graph::gt (const edge e) const
The classes GT_Graph_Attributes,
GT_Node_Attributes and GT_Edge_Attributes are
organized analogous to the layout of a graph, node or edge description in a
GML file. That is, there are some
attributes that are member variables of the class (such as id or label),
and others (such as graphics) that are organized in a separate class.
Those attributes that are implemented as member variables have the same type of access methods as other member variables throughout Graphlet. That is, the access methods have the same name as the attribute:
const string& GT_Node_Attributes::label () constvoid GT_Node_Attributes::label (const string&)The following example illustrates how to change the label of a node :
void GT_Sample_class::test_label (GT_Graph& g, node n)
{
// Retrieve the label of node n
const string& l = g.gt().label (n);
// Print it
cout << l << endl;
// Set the label of n to "new label"
g.gt(n).label ("new label");
}
Note: Graphlet's accessor methods come to full power with the attribute classes. To be update the drawing of a graph efficiently, Graphlet needs to know whenever an attribute has changed. The best way to do this is to forbid access with non-const references, and provide an extra method to change the value of an attribute. See also the section on how to detect wether an attribute has changed.
In the attribute compendium, we are using a slightly relaxed notion that immediately shows how to get or set attributes :
type gt_graph.gt().attribute; type gt_graph.gt(node).attribute(); type gt_graph.gt(edge).attribute();
void gt_graph.gt().attribute (type); void gt_graph.gt(node).attribute (type); void gt_graph.gt(edge).attribute (type);
In this notion, gt_graph is an object of class
GT_Graph, and node or edge are
placeholders for the node or edge. type is the class of
attribute, and is either a simple datatype (such as int or
double), or a constant reference to a class. An exception is
GT_Key, which is technically not a single datatype but does
not use references (because its actually implemented as a pointer to a key
description).
The graph, node and edge attributes defined in
GT_Graph_Attributes,
GT_Node_Attributes, and
GT_Edge_Attributes do not contain graphical attributes.
Instead, all graphical information is stored in the separate classes
GT_Graph_GraphicsGT_Graph_Label_GraphicsGT_Node_GraphicsGT_Node_Label_GraphicsGT_Edge_GraphicsGT_Edge_Label_Graphics
which are all based on the class
GT_Common_Graphics.
The methods graphics and label_graphics are used
to access the graphical attributes of nodes and edges. Both return
pointers to objects of one of the above classes:
void GT_Sample_class::show_coordinates (const GT_Graph& g, node n)
{
if (g.gt(n).graphics() != 0) {
cout << g.gt(n).graphics()->x())
<< " "
<< g.gt(n).graphics()->y())
<< endl;
}
}
Warning: Graphlet does not guarantee that these pointers are
not 0. Instead, every procedure which uses
graphics information must test wether the graphics are not 0.
The correct way to initialize graphics information is as follows:
if (g.gt().graphics() == 0) {
g.gt().graphics (g.new_graph_graphics());
}
node n;
forall_nodes (n, g) {
if (g.gt(n).graphics() == 0) {
g.gt(n).graphics (g.new_node_graphics());
}
}
edge e;
forall_edges (e, g) {
if (g.gt(e).graphics() == 0) {
g.gt(e).graphics (g.new_node_graphics());
}
}
graphics and label_graphicsThe access schemata for graphics attributes are analogous to those for graph, node or edge attributes :
type gt_graph.gt().graphics()->attribute(); type gt_graph.gt(node).graphics()->attribute(); type gt_graph.gt(edge).graphics()->attribute(); type gt_graph.gt().label_graphics()->attribute(); type gt_graph.gt(node).label_graphics()->attribute(); type gt_graph.gt(edge).label_graphics()->attribute();
void gt_graph.gt().graphics()->attribute (type); void gt_graph.gt(node).graphics()->attribute (type); void gt_graph.gt(edge).graphics()->attribute (type); void gt_graph.gt().label_graphics()->attribute (type); void gt_graph.gt(node).label_graphics()->attribute (type); void gt_graph.gt(edge).label_graphics()->attribute (type);
See the graphlet attribute kompendium for a complete list of graphics attributes.
The class GT_Tagged_Attribute provides a member parent which points to another oject of the same class. This other object is the parent of this object. The parent is an object that acts as a template that fills in values for all attributes which are not explicitely initialized.
For example, if the fill attribute in the graphics attributes has not been initialized, but fill has been initialized in the parent object of the graphics attributes, then the object gets the fill color as specified in the parent object. In this case, the attribute fill will have the same value as the attribute in the parent, and the flag initialized (and changed if appropriate) is set.
The member variable the_parent of the class
GT_Tagged_Variables is used to implement this. Parents are
used to implement Graphlet's style sheets.