Bringing Graph Elements to Life: The Realizer Concept

Graph elements are first and foremost structural elements. By themselves, they do not have a visual representation. Instead, the yFiles library utilizes so-called "realizers" to visualize the nodes and edges of a graph. Among other user interface aspects, a realizer defines the way a graph element is rendered. To this end, it has to be associated with, i.e., bound to, either node or edge, and thereafter it provides and manages all visual aspects of this graph element.

For either type of graph element there is a specialized realizer class available: class NodeRealizer for nodes, and class EdgeRealizer for edges. Figure 6.3, “Node and edge realizers” shows the class hierarchy for the realizer types. Both classes are abstract and serve as a basis for actual implementations. For most general cases there already exist a number of predefined node and edge realizers.

Figure 6.3. Node and edge realizers

Node and edge realizers.

Binding Realizers

The most convenient way to have realizers be bound to nodes and edges at creation time is to register so-called "default realizers" with a Graph2D object using the following methods. These realizers serve as factories for realizer creation by creating copies of themselves.

Graph elements that are created using any of the following methods are then automatically bound to the respective realizer type, i.e., to the copy of a default realizer:

Node createNode()
Node createNode(double x, double y)
Node createNode(double x, double y, double w, double h, String label)
Node createNode(double x, double y, String label)
Description Node creation methods from class Graph2D that resort to default realizer binding.
Edge createEdge(Node v, Node w)
Edge createEdge(Node v, Edge e1, Node w, Edge e2, int d1, int d2)
Description Edge creation methods from class Graph2D that resort to default realizer binding.

Alternatively, realizers can be bound to already existing nodes and edges on a per-element basis using the following setter methods. In effect, these methods are the most powerful to associate realizers.

The default realizer binding at element creation time can also be circumvented using these methods:

Node createNode(NodeRealizer r)
Description Node creation with explicit realizer binding.
Edge createEdge(Node v, Node w, EdgeRealizer r)
Edge createEdge(Node v, Edge e1,
                Node w, Edge e2, int d1, int d2, EdgeRealizer r)
Description Edge creation with explicit realizer binding.

General Features

Both node and edge realizers provide a number of common features that cover a variety of different aspects. The features include, e.g., support for multiple labels with a graph element, setting an element's graphical attributes, and handling its visibility and selection state. Other features, with a more technical focus, include, e.g., support for a special "less-detail" rendering mode and also for hit tests.

A realizer holds and manages an arbitrary number of labels. Its label support comprises methods to add or remove a label, to control a label's text, and to get the number of held labels. The realizers also provide factory methods to conveniently create the proper label type, i.e., either NodeLabel or EdgeLabel (see below). Furthermore, painting the labels lies in the responsibility of a realizer, too.

The label classes themselves are described in more detail in the section called “Label Support”. Among other things, the different positioning models for nodes and edges are explained there too.

NodeLabel createNodeLabel()
Description Factory method of class NodeRealizer to create node labels.
EdgeLabel createEdgeLabel()
Description Factory method of class EdgeRealizer to create edge labels.

Hit tests subsume a variety of related topics. These are:

  • Testing whether a mouse event's coordinates are inside the bounds of a graph element.
  • Calculating a graph element's bounding box.
  • Calculating a graph element's compound bounding box that also includes the bounding boxes of all its labels.
  • Determining whether a graph element is contained in a given rectangular area.
  • Determining intersection of a graph element's bounding box and a given rectangular area.

Methods for hit-testing in the realizer types:

Node Realizers

Node realizers are responsible for graphically rendering nodes in a view. Abstract class NodeRealizer provides the basis for actual implementations. Figure 6.4, “Node realizer hierarchy” shows the class hierarchy of the node realizer implementations.

Figure 6.4. Node realizer hierarchy

Node realizer hierarchy.

Table 6.1, “Predefined node realizer implementations” lists all predefined node realizer implementations from package y.view.

Table 6.1. Predefined node realizer implementations

Classname Description
GenericNodeRealizer A generic basis for convenient new-style customization of all aspects of node representation and behavior. Out of the box, this NodeRealizer provides only a simple default node representation.
ImageNodeRealizer A node representation that can draw an image.
ShapeNodeRealizer A node representation that can draw a variety of geometric shapes.

In addition to the realizer implementations in package y.view, the yFiles extension package ySVG, which adds support for Scalable Vector Graphics (SVG), makes available class SVGNodeRealizer. This realizer implementation enables using SVG content for the visual representation of nodes. See the section called “Using SVG Content” for more information.

Actual NodeRealizer implementations have to provide bodies for the following methods. These methods define the implementation's replication behavior, and how the node's visual representation is rendered. (Special considerations that have to be observed with realizer replication are described in Writing Customized Realizers.)

The following methods are invoked to render the visual representation of a node. The default "less-detail" implementation provided by class NodeRealizer draws the node as a simple rectangle. Note that all predefined node realizers resort to this default implementation.

Note

Further control over less-detail rendering for the elements in a view can be achieved by using rendering hints.

Support for hit tests is offered by the following methods. The default implementations of these methods take the bounding box of the node as the basis for their calculations. In contrast, note that method contains from class ShapeNodeRealizer performs shape-specific hit test computation. Also, note that method calcUnionRect calculates the enclosing rectangle of the node together with all its labels. Almost certainly, the result is larger than the bounding box of the node alone.

Class NodeRealizer supports specifying minimum size and maximum size constraints for the visual representation of a node by means of interface SizeConstraintProvider. Both size constraints are obeyed when a node is resized interactively by a user, i.e., when view mode class HotSpotMode handles the node resizing mouse gesture. The following method from class NodeRealizer is used by the view mode to retrieve custom implementations:

SizeConstraintProvider getSizeConstraintProvider()
Description Size constraints getter method.

Specifying a custom implementation differs depending on the actual realizer class that is used:

Class ShapeNodeRealizer

A NodeRealizer implementation where one of several possible geometric shapes is used for the visual representation of an associated node. Table 6.2, “Geometric shapes of class ShapeNodeRealizer” shows a list of all shape types together with their type constants as defined in class ShapeNodeRealizer.

Note that some of the geometric shapes behave differently, e.g.:

  • Shape type ELLIPSE is drawn with anti-aliasing turned on by default.
  • Shape type RECT_3D does not support transparency. Also, setting the line color has no effect, since there is no border line drawn.

Table 6.2. Geometric shapes of class ShapeNodeRealizer

Constant Name Shape
ELLIPSE
RECT
TRIANGLE
PARALLELOGRAM
HEXAGON
DIAMOND
OCTAGON
ROUND_RECT
RECT_3D
TRAPEZOID
TRAPEZOID_2

The shape type can be controlled using these methods:

byte getShapeType()
void setShapeType(byte type)
Description Shape-related methods from class ShapeNodeRealizer.

For tutorial demo code see:

Class ImageNodeRealizer

A NodeRealizer implementation where an image is used for the visual representation of an associated node. The image scales accordingly when the node gets resized. Figure 6.5, “Nodes represented by ImageNodeRealizer” shows a graph where ImageNodeRealizer is used.

Figure 6.5. Nodes represented by ImageNodeRealizer

Nodes represented by ImageNodeRealizer.

Class ImageNodeRealizer provides methods to specify the image using an URL or to give it directly. (Note that images that are specified by an URL have to have GIF or JPG image file format.)

Image getImage()
URL getImageURL()

void setImage(Image image)
void setImageURL(URL imageURL)
Description Image-related methods from class ImageNodeRealizer.

The alpha transparency of an image can optionally be used to modify the image's hit-testing behavior. Pixels of an image that are more translucent than opaque are considered irrelevant for the hit test. The setter method shown below can be used to determine whether alpha transparency should be considered:

boolean isAlphaImageUsed()
void setAlphaImageUsed(boolean use)
Description Alpha transparency support for images.

Furthermore, the class holds an image cache where all URL-specified images are stored so that they can be retrieved from this cache. The image cache can conveniently be filled using a number of static methods:

Note

The URL that has been used to specify a node's image is also used when the graph containing the node is written to file. To achieve uniqueness, however, the URL is made absolute.

Class GenericNodeRealizer

This NodeRealizer implementation primarily serves as a generic basis for node realizer customization. It defines a set of static inner interfaces which allow fine-grained control over all aspects of a node realizer's visual representation and behavior. Instead of applying subclassing and overriding, custom logic for a specialized realizer can be conveniently expressed by providing implementations for only a selection or for all of these interfaces.

Class GenericNodeRealizer offers all methods from its superclass NodeRealizer, the actual work, though, is delegated to the following interfaces:

Together, a set of actual implementations for these interfaces forms a so-called "configuration" that defines the look and feel of one node type. Different configurations can be used to define a variety of node types. Within a configuration, different settings for GenericNodeRealizer instances can be easily achieved using so-called style properties.

Table 6.3, “Predefined GenericNodeRealizer.Painter implementations” lists predefined implementations for interface GenericNodeRealizer.Painter.

Table 6.3. Predefined GenericNodeRealizer.Painter implementations

Classname Description
ShapeNodePainter Provides the same geometric shapes as depicted in Table 6.2, “Geometric shapes of class ShapeNodeRealizer” for the visual representation of nodes.
ImageNodePainter Uses an image for the visual representation of nodes. The services provided by this implementation are similar to those available with class ImageNodeRealizer.
SVGPainter Enables using SVG content for the visual representation of nodes. See also the section called “Using SVG Content”.
GeneralPathNodePainter Enables using arbitrary GeneralPath objects for the visual representation of nodes.
NodeCellRendererPainter Enables using Swing UI components for the visual representation of nodes. See below for an example.
BevelNodePainter Can be used to render rectangular nodes with rounded corners, a bevel border, and a shining background. Optionally, a simple drop shadow can be rendered, too.
ShinyPlateNodePainter Can be used to render rectangular nodes with rounded corners and a 'shiny plate' interior. Optionally, a simple drop shadow can be rendered, too.
ShadowNodePainter Serves as a decorator for other GenericNodeRealizer.Painter implementations and adds a drop shadow effect.

Note

Except SVGPainter, NodeCellRendererPainter, and ShadowNodePainter, all implementations also implement interface GenericNodeRealizer.ContainsTest.

Figure 6.6, “Sample renderings using predefined GenericNodeRealizer.Painter implementations” shows examples of nodes using ShapeNodePainter, BevelNodePainter, and ShinyPlateNodePainter, respectively. BevelNodePainter and ShinyPlateNodePainter optionally render a drop shadow by themselves, ShapeNodePainter can be decorated by means of ShadowNodePainter to also have a drop shadow. Tutorial demo application GenericNodeRealizerDemo.java shows how to use the predefined implementations.

Figure 6.6. Sample renderings using predefined GenericNodeRealizer.Painter implementations

Sample renderings using predefined GenericNodeRealizer.Painter implementations
Sample renderings using predefined GenericNodeRealizer.Painter implementations

Using class NodeCellRendererPainter as the GenericNodeRealizer.Painter implementation in a configuration, enables rendering of Swing UI components as seen in Figure 6.7, “Swing UI component rendered by GenericNodeRealizer”.

Figure 6.7. Swing UI component rendered by GenericNodeRealizer

GenericNodeRealizer configuration that renders Swing UI components.

The tutorial demo application SwingRendererDemo.java shows how such a configuration can be defined. (See also Swing User Interface Components as Node Realizers for more information on the Swing UI components rendering support provided by the yFiles library.)

Out of the box, GenericNodeRealizer uses its default configuration, which results in a simple rectangular node representation similar to that of ShapeNodeRealizer as shown above. To take full advantage of the concept that is behind class GenericNodeRealizer, custom configurations must be defined and registered. The section Writing Customized Realizers discusses the necessary steps.

Edge Realizers

Edge realizers are responsible for graphically rendering edges in a view. Abstract class EdgeRealizer provides the basis for actual implementations. Figure 6.8, “Edge realizer hierarchy” shows the class hierarchy of the edge realizer implementations.

Figure 6.8. Edge realizer hierarchy

Edge realizer hierarchy.

Table 6.4, “Predefined edge realizer implementations” lists all predefined edge realizer implementations from package y.view.

Table 6.4. Predefined edge realizer implementations

Classname Description
GenericEdgeRealizer A generic basis for convenient new-style customization of all aspects of edge representation and behavior. Out of the box, this EdgeRealizer provides only a simple default edge representation.
ArcEdgeRealizer An edge representation that draws a simple arc.
BezierEdgeRealizer An edge representation that draws a spline curve where the existing bends are interpreted as control points for the curve. No control point actually lies on the edge path.
PolyLineEdgeRealizer An edge representation that draws direct line connections from bend to bend.
QuadCurveEdgeRealizer An edge representation that draws a spline curve where the existing bends are interpreted as control points for the curve. No control point actually lies on the edge path.
SplineEdgeRealizer An edge representation that draws a spline curve where the existing bends are interpreted as control points for the curve. All control points lie on the edge path.

Actual EdgeRealizer implementations have to provide bodies for the abstract methods listed below. These methods determine bend handling, edge path calculation, and also the implementation's replication behavior. (Special considerations that have to be observed with realizer replication are described in Writing Customized Realizers.)

Note

"Bend" and "control point" are two terms for the same thing; class Bend defines the visual counterpart for a control point in an edge path.

The following methods are invoked to render the visual representation of an edge. The default "less-detail" implementation provided by class EdgeRealizer draws the edge as a simple poly-line path without any decorations and labels. Note that all predefined edge realizers resort to this default implementation.

Note

Further control over less-detail rendering for the elements in a view can be achieved by using rendering hints.

Support for hit tests is offered by the following methods. Note that the intersection methods are mainly used to determine whether an edge lies inside a given rectangular area. In contrast, the hit-testing methods check whether a mouse event's coordinates lie on the edge's path.

Class PolyLineEdgeRealizer

An EdgeRealizer implementation where the associated edge is drawn

  • as a straight line segment connecting its actual end points (if the edge does not have any bends), or
  • as a sequence of straight line segments that forms a path connecting end points and the bend(s) in-between (if the edge has at least one bend).

Figure 6.9, “Edge represented by PolyLineEdgeRealizer” shows an example of a poly-line edge that has six bends and hence seven edge segments. (The straight line segments of an edge are called "edge segments.")

Figure 6.9. Edge represented by PolyLineEdgeRealizer

Edge represented by PolyLineEdgeRealizer.

Class PolyLineEdgeRealizer provides a bend-smoothing feature which can be controlled using the following methods:

boolean getSmoothedBends()
void setSmoothedBends(boolean b)
Description Getter and setter method of class PolyLineEdgeRealizer for bend-smoothing.

A special rendering behavior is applied for self-loops with at most one bend:

Self-loops that have more than one bend are drawn normally.

Figure 6.10. Automatic self-loop rendering behavior with class PolyLineEdgeRealizer

Default self-loop rendering.
Semi-default self-loop rendering.
Bend-less self-loop. Single-bend self-loop.

Figure 6.11. Standard self-loop rendering behavior

Standard self-loop rendering behavior
Standard rendering for a self-loop with more than one bend.

Note

The applied "less-detail" rendering does neither smoothed bends nor special self-loop rendering.

Class ArcEdgeRealizer

An EdgeRealizer implementation where the associated edge is drawn as a simple arc connecting its end nodes. The arc is drawn using a single bend that always lies on the perpendicular bisector of the straight line segment between the arc's actual end points. Figure 6.12, “Edge represented by ArcEdgeRealizer” shows an arc example.

Figure 6.12. Edge represented by ArcEdgeRealizer

Edge represented by ArcEdgeRealizer.

As a consequence of the single-bend design all excessive bends of an edge are actually deleted when it is associated an instance of type ArcEdgeRealizer. Also, once an edge is associated an ArcEdgeRealizer instance, the number of bends cannot be changed. The bend-handling methods in class ArcEdgeRealizer have empty bodies:

Class ArcEdgeRealizer supports two different arc behaviors: fixed height and fixed ratio (height to distance). The behavior defines the way the arc is drawn when the position of one of the arc's end nodes changes.

Fixed height means that the height of the perpendicular of the straight line segment between the arc's actual end points remains fixed regardless of the distance between the end points. In contrast, fixed ratio means that the ratio of height to distance remains fixed.

Figure 6.13, “ArcEdgeRealizer behaviors” demonstrates the difference in behavior when the distance between the arc's end points is reduced. Fixed height behavior retains the arc's height, while fixed ratio yields a scaled version of the original arc.

Figure 6.13. ArcEdgeRealizer behaviors

Arc with fixed height behavior.
Arc with fixed ratio behavior.
Fixed height. Fixed ratio.

The following methods of class ArcEdgeRealizer can be used in relation with arc behavior:

byte getArcType()
void setArcType(byte t)
Description Getting/setting the arc behavior.
float getHeight()
float getRatio()
void setHeight(float h)
void setRatio(float r)
Description Getter and setter methods for either arc behavior.

Class BezierEdgeRealizer

An EdgeRealizer implementation where the associated edge is drawn as a bezier spline curve. The edge's bends are interpreted as control points for the curve, which, by construction, are not part of the curve path.

Figure 6.14, “Edge represented by BezierEdgeRealizer” shows an example of a bezier spline curve rendered by class BezierEdgeRealizer. The edge is selected to point out the effect of the original bends that are interpreted as control points. (Observe also, that the selection indication for a bezier spline curve applies special rendering to present the control points.)

Figure 6.14. Edge represented by BezierEdgeRealizer

Edge represented by BezierEdgeRealizer.

Class SplineEdgeRealizer

An EdgeRealizer implementation where the associated edge is drawn as a cubic spline curve. The edge's bends are interpreted as control points for the curve, which, by construction, are all part of the curve path.

Figure 6.15, “Edge represented by SplineEdgeRealizer” shows an example of a cubic spline curve rendered by class SplineEdgeRealizer. The edge is selected to point out the effect of the original bends that are interpreted as control points.

Figure 6.15. Edge represented by SplineEdgeRealizer

Edge represented by SplineEdgeRealizer.

Class QuadCurveEdgeRealizer

An EdgeRealizer implementation where the associated edge is drawn as a quadratic spline curve. The edge's bends are interpreted as control points for the curve, which, by construction, are not part of the curve path.

Class QuadCurveEdgeRealizer supports a straightness factor to customize the extent at which the curve follows the given straight line path between control points. Having a range of 0.0 to 1.0, higher straightness factor means closer matching of curve and given straight line path. The following methods control the straightness settings for a quadratic spline curve:

double getStraightness()
void setStraightness(double straightness)
Description Straightness methods from class QuadCurveEdgeRealizer.

Figure 6.16, “Edges represented by QuadCurveEdgeRealizer” demonstrates two possible quadratic spline curves rendered by class QuadCurveEdgeRealizer. The edges are selected to point out the effect of the original bends that are interpreted as control points. (Observe also, that the selection indication for a quadratic spline curve applies special rendering to present the control points.)

Figure 6.16. Edges represented by QuadCurveEdgeRealizer

Quad curve with straightness = 0.0
Quad curve with straightness = 0.5
Straightness = 0.0 Straightness = 0.5

Class GenericEdgeRealizer

This EdgeRealizer implementation primarily serves as a generic basis for edge realizer customization. It defines a set of static inner interfaces which allow fine-grained control over all aspects of an edge realizer's visual representation and behavior. Instead of applying subclassing and overriding, custom logic for a specialized realizer can be conveniently expressed by providing implementations for only a selection or for all of these interfaces.

Class GenericEdgeRealizer offers all methods from its superclass EdgeRealizer, the actual work, though, is delegated to the following interfaces:

Together, a set of actual implementations for these interfaces forms a so-called "configuration" that defines the look and feel of one edge type. Different configurations can be used to define a variety of edge types.

Table 6.5, “Predefined GenericEdgeRealizer.PathCalculator implementations” lists predefined implementations for interface GenericEdgeRealizer.PathCalculator.

Table 6.5. Predefined GenericEdgeRealizer.PathCalculator implementations

Classname Description
PolyLinePathCalculator Provides the same path calculation logic that yields a poly-line edge path as depicted in the section called “Class PolyLineEdgeRealizer”.
SmoothBendsPathCalculator Serves as a decorator for other GenericEdgeRealizer.PathCalculator implementations and adds bend-smoothing to the path.

Out of the box, this edge realizer uses its default configuration, which results in a simple poly-line edge representation similar to that of PolyLineEdgeRealizer as shown above. To take full advantage of the concept that is behind class GenericEdgeRealizer, custom configurations must be defined and registered. The section Writing Customized Realizers discusses the necessary steps.