Graph View

This interface defines a view of a Graph model to which instances must always be attached. A GraphView is associated n:1 with this model object.

Objects of type Graph, Node and Edge represent the model entities in the graph domain. These entities may be associated on a one-to-many basis with objects whose types are used for the visualization inside a view. Simply put, each model class has a visual counterpart whose instances may reference exactly one model object.

The following class diagram shows the visual component structure and dependencies in the Tensegrity Graph Framework.

The VisualGraph distinguishes among three kinds of VisualGraphObject classes:

When you add a VisualGraphObject to a view, its identifier is automatically assigned. Before adding, it, however, this identifier should be set to -1. Upon removal, this identifier will be reset to -1.

Each VisualGraphObject may only be added to at most one VisualGraph or VisualSubgraph at a time. Attempting to add the same object to more than one VisualGraph or VisualSubgraph will cause an exception to be thrown.

Besides adding and removing VisualNode and VisualEdge instances to and from a VisualGraph, you should be aware of a few notable methods defined in the VisualGraph interface:

A visual graph type is closely coupled with the following graph model types:

Model objectAssociated view object
Graph VisualGraph
GraphObject VisualGraphObject
Node VisualNode
Subgraph VisualSubgraph
Edge VisualEdge
Port VisualPort
PortDenotation VisualPortDenotation

Every time a view object is added to a VisualGraph, an associated model object must be already present in the Graph model. Every time a VisualGraph object is removed, a model object must also be present as well. These conditions can be summarized as follows:

VisualGraph objects can be nested inside another VisualGraph to any depth whatsoever. This is possible because the interface specification for VisualSubgraph derives from the interface VisualNode.

It is possible to traverse a hierarchy of VisualSubgraph objects by invoking the method visitSubgraphs(VisualGraphVisitor) and providing a custom callback object that implements interface VisualGraphVisitor. VisualSubgraph objects can be nested inside each other recursively. The nesting hierarchy, however, may not contain cycles.

Several methods in this interface specification are inherited from the base interface VisualGraphObjectContainer, which specifies generic handling of VisualGraph and VisualSubgraph objects.

It is recommended that you configure and customize the behavior of a VisualGraph by using the rules facility only. The rules package offers the most flexible way of configuring VisualGraphs. Therefore a VisualGraph supports the registration of a RuleRegistry. In the context of VisualGraphs client code should register an instance of a RuleRegistry ONLY at the top-level VisualGraphView. This RuleRegistry will be subsequently registered to all the added VisualGraphObjects and nested VisualSubgraphs automatically by the GraphAPI. This means that there can be only one active RuleRegistry within the entire hierarchy of VisualGraphs. For further information about the usage please see the documentation of RuleRegistry. The methods a VisualGraph provides in order to support the RuleRegistry are listed below:

VisualGraph Interfaces and Classes

Each model object can be referenced by multiple view objects which implement the functionality that allows them to be visualized. This implicates that view objects have coordinates and regions associated with them. Additionally, they have a notion of being selectable and resizable, actions that are typically found in visual editing. Finally, certain view classes, such as VisualNode and VisualEdge, extend interfaces which allow them to be automatically positioned by the layout engine. We will discuss these interfaces in more detail later.

The visualization of a view object is done with the help of a so-called Composite, a compound graphical object built upon Primitive parts. As you might have already guessed, these objects are defined by the two Java® interfaces Composite and Primitive. A graphical style, such as a stroke, fill pattern or font, can be assigned to each graphical Primitive where appropriate. These objects can then be grouped inside any Composite parent container. In this way, primitives can be used to compose complex graphical shapes.

In order to build even more complex visual shapes, ones that represent entire visual subgraphs, we provide a specialized, derived interface named CompositeGroup. Instances of this type are also Composite objects capable of nesting other Composite objects as well, providing you with the ability to nest arbitrarily complex levels of primitives.

There is a close dependency between the visually-scoped objects from the Graph library and the Composite library. For example, objects of type VisualNode use Composite instances for their rendering. The Tensegrity Graph Framework requires that each VisualNode instance be associated 1:1 with a Composite instance. Of course, this instance may be any subtype implementation as well. In addition, instances of type VisualSubgraph have a 1:1 association with an an instance of type CompositeGroup.

Table 1.2. View - Composite Associations

Visual ClassAssociated CompositeDescription
VisualNodeComposite Each VisualNode instance is associated with a Composite instance.
VisualEdgeCompositeLine Each VisualEdge instance is associated with a CompositeLine instance.
VisualPortComposite Each VisualPort instance is associated with a Composite instance (but not on a fixed 1:1 basis).
VisualSubgraphCompositeGroup Each instance of VisualSubgraph is associated with a CompositeGroup instance. Since interface VisualSubgraph extends the VisualNode interface, it is also possible to obtain the Composite instance that each VisualNode (and hence each VisualSubgraph) is associated with. The fact that interface CompositeGroup extends the Composite interface reflects this substitution functionality.

Figure 1.2. Classdiagram Graph View - Composite Associations

Classdiagram Graph View - Composite Associations

The association between a Composite and a view object is permanent. This means that once a view object has been created, its reference to the associated Composite can no longer be changed.

Note

Instances of VisualPort are an exception in that they are not associated with Composite objects on a fixed 1:1 basis.

Interface VisualGraphObject

This interface represents the aspects common to all visual graph elements, including a VisualNode, VisualEdge and VisualSubgraph. The first of these aspects includes the identifiable nature of these elements. Every VisualGraphObject has both an identifier unique to its context as well as a global identifier that is unique within a hierarchy of VisualGraph objects. Methods to access both of these identifiers are available.

Each VisualGraphObject implementation is constructed with an initial identifier of -1. When a VisualGraphObject is added to a GraphObjectContainer, the identifier is changed internally by the container to a value that becomes unique within that container context. The container may nest other containers recursively, which might have the same identifier assigned to one of their managed objects. Identifiers, therefore, are unique within the scope of a single GraphObjectContainer only.

When a VisualGraphObject is removed from a GraphObjectContainer, the identifier is reset to -1.

Each VisualGraphObject can only be stored in one dedicated VisualGraphObjectContainer at a time.

The identifiers that are internally assigned to a VisualGraphObject corresponds with the identifier of the associated model object. In this way, a mapping from model to view objects is possible. You can create multiple VisualGraphObject instances that all reference the same model object.

Moreover, objects of this type are represented by a Composite, reference a Graph model and possess behavior specified by custom Rule objects. [2].

The following a list of the important methods offered by this interface:

  • BaseComposite getBaseComposite()

    Returns the instance of the BaseComposite that is associated to the VisualGraphObject instance.

  • GraphObject getGraphObject()

    Returns the instance of the GraphObject that corresponds to the VisualGraphObject instance.

  • long getID()

    Returns the id of this VisualGraph object. The id is local to the VisualGraph and exists as well in the associated graph-model objects. The id however is unique in the scope of VisualGraphObjects of a single VisualGraph.

  • VisualGraphObjectContainer getParentContainer()

    This method returns the VisualGraphObjectContainer this VisualGraphObject instance currently resides in. In case this VisualGraphObject instance is not added to a VisualGraphObjectContainer this method returns null.

  • void setPinned(boolean)

    Marks this VisualGraphObject as pinned.

  • VisualGraphObjectContainer getRootContainer()

    VisualGraphObjectContainer instances can be nested inside of each other by means of the VisualSubgraph class. This method returns the root component of the nesting hierarchy, or the this pointer if this instance is the root of the hierarchy. In case this instance is not added to a parent VisualGraphObjectContainer null is returned.

  • String getRule()

    Gets the rule associated with this object.

  • void setRule(String)

    Sets the rule name associated with this object.

  • RuleRegistry getRuleRegistry()

    Retrieves the active RuleRegistry for this VisualGraphObject instance.

  • void setRuleRegistry(RuleRegistry)

    Stores the active RuleRegistry of the rule in the visualgraphobject, so that it can be traced back.

  • long getUniqueID()

    Returns the global id of this VisualGraphObject instance. The returned id is unique within the complete graph hierarchy and is the same as the unique id of the associated GraphObject.

  • boolean isPinned()

    Return a boolean that indicates whether this VisualGraphObject is pinned.

VisualGraphObject instances are visual representations of GraphObject instances. Moreover, these visual types extend interface Layoutable so that they may be automatically positioned by a LayoutController.

Caution

Methods intended for internal use only:

  • void registerVisualEventMediator()

  • void registerVetoableVisualEventMediator()

Interface VisualNode

This interface represents the functionality of any potential Node representation. By incorporating the functionality of a Composite object, it becomes possible to assign complex shapes and styles to an instance of this class.

The associated model Node can be obtained by invoking the getNode() method.

Here are important methods defined in the interface:

  • void adjustEdges()

    Updates all adjacent edgs which are registered at ports of this visualnode. The edge will snap in to their ports in case their positions differ from their attached positions.

  • Composite getComposite()

    Returns the instance of the Composite associated to the VisualNode instance.

  • Node getNode()

    Returns the reference to the node this VisualNode is associated with. (there is a 1:n relationship between Nodes and VisualNodes)

  • VisualNodeInfo getVisualNodeInfo()

    Returns the VisualNodeInfo associated with this VisualNode.

  • List getVisualPorts()

    Returns a list of the VisualPorts of this VisualNode.

Interface VisualEdge

This interface represents the functionality of any potential Edge representation. By incorporating the functionality of a Composite object, it becomes possible to assign complex shapes and styles to an instance of this class.

Here are the most important methods defined in the interface:

  • CompositeLine getComposite()

    Returns the instance of the CompositeLine associated to the VisualEdge instance.

  • Edge getEdge()

    Returns the reference to the Edge this VisualEdge is associated with. (1:n relationship between edges and VisualEdges)

  • VisualEdgeInfo getVisualEdgeInfo()

    Returns the VisualEdgeInfo associated with this VisualEdge.

  • VisualNode getVisualSource()

    Gets the VisualNode at the source end of this VisualEdge.

  • VisualPort getVisualSourcePort()

    Returns the VisualPort VisualPort at the source end of this instance. The field may be null if the VisualEdge is currently detached.

  • VisualNode getVisualTarget()

    Gets the VisualNode at the target end of this VisualEdge.

  • VisualPort getVisualTargetPort()

    Returns the VisualPort VisualPort at the target-end of this instance. The field may be null if the VisualEdge is currently detached.

  • void updatePosition()

    This method updates the position of this VisualEdge instance according to the ports it is connected to. If this VisualEdge is not connected to a VisualPort then invoking the method will not trigger and action.

Interface VisualPort

This interface represents the visualization of a model Port that is contained and managed by a VisualNode.

This interface defines the characteristics of a single visual connection point to a VisualNode from which a VisualEdge instance is attached. In other words, an VisualEdge connects to a VisualNode via one of the many VisualPort instances owned and managed by a VisualNode, and not to the VisualNode directly.

A VisualPort uses a so-called VisualPortDenotation, which defines the angular interval where incoming and outgoing edges are allowed to connect to the associated VisualPort.

Visual ports have identifiers which can be returned to clients who request them.

The identifiers of a VisualPort and its associated model object always correspond and are unique within a single Graph, VisualGraph, Subgraph and VisualSubgraph. These identifiers are set to -1 when no longer contained by a parent component.

Each active VisualPort managed by a single VisualNode instance can be retrieved by invoking the method getVisualNode().

The number of VisualEdge objects that are currently connected to a VisualPort can be retrieved by invoking the method getRegisteredVisualEdgeCount(). The actual VisualEdge objects that are connected can be retrieved by invoking getRegisteredVisualEdges().

A VisualPort can have a number of different drawing states which are defined by the constants prefixed with DRAWINGSTATE_ and which can be set by invoking the method setDrawingState(int). This method is invoked internally by the library and usually there should not be any to need to invoke this method manually.

A VisualPort can either be direct (a so-called leaf-port) or indirect (a so-called wrapped-port). From the perspective of a library user, the difference is not significant. Wrapped-ports can themselves be wrapped up recursively. The method getDepth() can be used to find out the depth of nesting. The VisualNode that holds the leaf-port (the port that doesnt wrap-up other ports) can be queried by invoking the method getWrappedVisualNode().

The methods getCoordinate() and getBoundingBoxCoordinate() are used to find out the geometric position of a VisualPort. The later method returns the position in the CoordinateSystem of the enclosing VisualNode and thus is the one that is likely to be used in most cases.

Interface VisualGraphObjectContainer

This interface holds the methods that define the container role for classes that manage VisualGraphObject instances. This is the parent interface of both the VisualGraph and VisualSubgraph interfaces.

A VisualGraphObjectContainter will manage VisualNode and VisualEdge instances by providing implementations to add and remove objects of these types.

  • void addVisualEdge(VisualEdge)

    Adds a new VisualEdge to this VisualGraphObjectContainer.

  • GraphObjectContainer getGraphObjectContainer()

    Gets the associated GraphObjectContainer of this instance. The model instance can be associated with one or multiple VisualGraphObjectContainer instances.

  • VisualGraphObjectContainer getParentContainer()

    VisualGraphObjectContainer instances can be nested inside of each other by means of the VisualSubgraph class. This method returns the parent component of this VisualGraphObjectContainer or null if this instance is the root of the hierarchy.

  • VisualGraphObjectContainer getRootContainer()

    VisualGraphObjectContainer instances can be nested inside of each other by means of the VisualSubgraph class. This method returns the root component of the nesting hierarchy, or the this pointer if this instance is the root of the hierarchy. In case this instance is not added to a parent VisualGraphObjectContainer null is returned.

  • Iterator getVisualEdges()

    Retrieves an iterator for all properly connected VisualEdge in the VisualGraphObjectContainer.

  • Iterator getVisualGraphObjects()

    Retrieves an iterator for all VisualGraphObjects.

  • Iterator getVisualNodes()

    Retrieves an iterator for all visualnodes.

  • void removeVisualEdge(VisualEdge)

    Removes a VisualEdge from the VisualGraphObjectContainer. The VisualEdge must be in the graph for this operation to succeed.

  • void visitSubgraphs(VisualGraphVisitor)

    Recurse over all nested subgraphs starting from this visualsubgraph.

Interface VisualGraphView

This interface defines a view of a Graph model as well but adds additional functional specifications. Implementations of class VisualGraphView represent the top-level containers within a hierarchy of nested VisualGraphObjectContainer instances and are responsible for the undo, redo, cut, copy and paste functionalities within a graph document.

VisualGraphView derives from and thus aggregates many interfaces, including VisualGraph, CompositeView and the VisualGraphObjectContainer interfaces, allowing clients to treat a VisualGraphView as one of many specific roles.

Also, it is important to note that a new VisualGraphView instance requires a reference to a GraphController, which must be advised to manage the newly created view after instantiation.

When new visual edges are introduced via edge-splitting, either manually or by user interaction, these new edges will be assigned a default style. This style can be configured for the entire view hierarchy by invoking the setDefaultEdgeStyle(String) method.

The paste orientation specifies how pasting preserves the relative order of connected VisualNode objects. See method setPasteOrientation(int).

VisualEdge update modes determine how connected VisualEdge objects should behave when the position of a VisualNode changes. Update modes can be set with method setVisualEdgeUpdateMode(int).

A VisualGraphView supports user interaction with the mouse. To do so it provides the methods listed below:

  • InteractionHighlightConfiguration getInteractionHighlightConfiguration()

    Returns the InteractionHighlightConfiguration instance that is used within this VisualGraphView to identify the geometries and styles for highlighting interactions like edge-splitting etc. The returned reference can then be manipulated in order to change styles and geometries.

  • InteractionSettings getInteractionSettings()

    Returns the current interaction settings class from this VisualGraphView. Interaction settings have a higher priority than the results of the callbacks from the active InteractionControl class.

Other important methods defined in this interface are:

  • LayoutController getLayoutController()

    Gets the LayoutController that is associated with this instance. May be null.

  • void setVisualEdgeUpdateMode(int)

    Sets the update-mode for VisualEdges. The update-mode determines the behavior of VisualEdges when any of the VisualNodes they are connected to are moved.

    1. UPDATEMODE_SIMPLE Update only fully connected VisualEdges. If a VisualEdge is not fully connected on both ends, then moving the VisualNode that has one end connected wont change any of the VisualEdges coordinates.

    2. UPDATEMODE_PLAIN Similar to update mode UPDATEMODE_SIMPLE, but it will also update objects of type VisualEdge that are connected to exactly one of the ends of the corresponding VisualNode.

    3. UPDATEMODE_ORTHOGONAL Keep care of orthogonal (parallel to x or y-axis) segments of the edge and distributes the changes horizontally and vertically to those segments, thus preserving orthogonality.

Interface GraphViewFactory

This abstract class defines the methods to create instances of all graph view objects.

By calling the static method newInstance(), you can obtain an instance that is assignable to this class.

A concrete implementation of this abstract class is GraphViewFactoryImpl. The following list contains a subset of the methods in this class.

  • VisualEdge newVisualEdge(Edge, VisualNode, VisualNode)

    Creates a new VisualEdge associated with the given model edge. The edge is connected to the given source and target VisualNode object.

  • VisualGraph newVisualGraph(Graph)

    Creates a new VisualGraph associated with the given model graph.

  • VisualNode newVisualNode(Node, List, Composite)

    Creates a new VisualNode associated with the given model node and providing the given ports. This VisualNode will be graphically represented by the given Composite.

  • VisualPort newVisualPort(VisualPortDenotation, int, int, Port)

    Creates a new VisualPort associated with the given model port and further described by denotation. It will be located at position (coordHorizontal, coordVertical.

  • VisualPortDenotation newVisualPortDenotation(double[])

    Creates a new VisualPortDenotation using the given interval. Parameter boundedInterval has to be double-array of length 2, where the value stored at index 0 describes the beginning of the interval and the value stored at index 1 describes the end of the interval.

  • VisualSubgraph newVisualSubgraph(Subgraph, VisualGraph, CompositeGroup)

    Creates a new VisualSubgraph that will be nested within the given VisualGraph and visualized by the CompositeGroup given by parameter compositegroup.

Beside method newInstance the following static methods are provided to ease the VisualNode creation process.

  • VisualNode makeDefaultVisualNode(GraphViewFactory, Node, Composite)

    This method creates a default VisualNode instance. The instance is configured in a default way. A set of default ports is assigned to the VisualNode. Typically this set of default ports includes four ports at each side of the rectangular bounding box of the VisualNode and an additional port in the middle.

  • VisualNode makeDefaultVisualNode(GraphViewFactory, Node, Composite, VisualNodeInfo)

    This method creates a default VisualNode instance and associates a VisualNodeInfo object with it. The instance is configured in a default way. A set of default ports is assigned to the VisualNode. Typically this set of default ports includes four ports at each side of the rectangular bounding box of the VisualNode and an additional port in the middle. The given VisualNodeInfo is associated to the returned VisualNode.

Creating a VisualGraph

Each and every view object must be associated with a corresponding model object. As a consequence, the creation of a view object cannot take place before the creation of its referenced model object. Along similar lines, no view objects may be added to a VisualGraph whose model objects do not belong to same Graph referenced by the VisualGraph

This system of adding model and view objects and later removing them follows the LIFO (Last in, First out) system. Before you can remove a model object from a Graph, you must first remove all view objects that reference it from their parent view containers.

If you are not creating objects of type Subgraph and VisualSubgraph, you will also not be using interface type VisualGraph directly. Instead, the interface type VisualGraphView is used whenever an actual visualization (including painting) is required. The following code snippet illustrates just how to create an empty VisualGraphView with a typical configuration.

Example 1.3. Assembling A Simple VisualGraphView

VisualGraphView createVisualGraphView()
{
    // set up references to the factories
    GraphModelFactory modelfactory= 
        GraphModelFactory.newInstance();
    GraphViewFactory viewfactory= 
        GraphViewFactory.newInstance();
    GraphControllerFactory controllerfactory= 
        GraphControllerFactory.newInstance();

    // create a model-scope Graph
    Graph graph= modelfactory.newGraph();

    // create a default controller
    GraphController clientServerController= 
        controllerfactory.newClientServerGraphController(graph);
    
    VisualGraphView visualgraphview= 
        viewfactory.newVisualGraphView(clientServerController);
    
    // configure the visualgraphview in a typical way
    visualgraphview.setBoundingBox(0, 0, 0, 0);
    visualgraphview.enableClipping(true);
    visualgraphview.setSelectPastedElements(true);

    InteractionSettings settings= 
        visualgraphview.getInteractionSettings();
    settings.setPortsEnabled(true);
    settings.setAllowLoops(true);
    visualgraphview.setDefaultEdgeStyle("TenseLine");
    
    return visualgraphview;
}

This code not only creates a VisualGraphView instance but also the underlying model container of type Graph. The resulting structure is capable of representing model-scoped Node and Edge objects as part of the view representation.

Adding and Removing View Objects

As mentioned before, any view object can only be added if its associated model object has already been added to its corresponding model Graph. Suppose, for example, that we have an instance of type VisualGraphView that was created with the code provided in the previous example and stored in a local variable called visualgraphview. Adding two nodes and connecting them with an edge, in both the model and the view, would require the following code:

Example 1.4. Adding VisualNodes and VisualEdges

void addObjects(VisualGraphView visualgraphview) throws StaticException
{
    // set up factory references
    GraphModelFactory modelfactory= 
        GraphModelFactory.newInstance();
    GraphViewFactory viewfactory= 
        GraphViewFactory.newInstance();
    
    // first we create the nodes and edges
    Node node1= 
        GraphModelFactory.makeDefaultNode(modelfactory, "node1");
    Node node2= 
        GraphModelFactory.makeDefaultNode(modelfactory, "node2");

    VisualNode visualNode1= viewfactory.newVisualNode(node1,
        GeometryPool.get("Rectangle"));
    VisualNode visualNode2= viewfactory.newVisualNode(node2,
        GeometryPool.get("Rectangle"));
    Edge edge= modelfactory.newEdge(node1, node2);
    VisualEdge visualEdge= 
        viewfactory.newVisualEdge(
            edge, visualNode1, visualNode2);
    
    // add first node
    visualgraphview.getGraph().addNode(node1);
    visualgraphview.addVisualNode(visualNode2);
    
    // add second node
    visualgraphview.getGraph().addNode(node2);
    visualgraphview.addVisualNode(visualNode1);
    
    // add edge
    visualgraphview.getGraph().addEdge(edge);
    visualgraphview.addVisualEdge(visualEdge);
}

Note

Please note the order in which objects are added. First a model object is added, then the associated view object.

The following code illustrates how to remove the objects that were added in the previous example.

Example 1.5. Removing VisualNodes and VisualEdges

void removeObjects(VisualGraphView visualgraphview)
        throws StaticException
{
    // remove edge
    VisualEdge visualEdge= 
        (VisualEdge) visualgraphview.getVisualEdges().next();
    visualgraphview.removeVisualEdge(visualEdge);
    visualgraphview.getGraph().removeEdge(visualEdge.getEdge());
    
    // remove first node
    VisualNode tmpNode= 
        (VisualNode) visualgraphview.getVisualNodes().next();
    visualgraphview.removeVisualNode(tmpNode);
    visualgraphview.getGraph().removeNode(tmpNode.getNode());
    
    // remove another node
    tmpNode= 
        (VisualNode) visualgraphview.getVisualNodes().next();
    visualgraphview.removeVisualNode(tmpNode);
    visualgraphview.getGraph().removeNode(tmpNode.getNode());
}

Please note the order when removing objects. First a view object is removed, then the associated model object.



[2] See section Modeling Rules of chapter Elements