Composites are compound graphical objects. Within the Tensegrity Graph Framework, they are used
to visualize the fairly complex representations of nodes and edges. In order
to reduce such complexity at the programming or specification levels, composites
are constructed using one or more Primitive objects. In and of themselves,
Primitive objects are fairly simple and straightforward geometric shapes.
To assist developers in using and creating even more complex and interactive composites, the Tensegrity Graph Framework allows you to implement and integrate user-defined composite classes which share these basic characteristics:
Composites may contain any number of simple, Primitive objects.
All composites (with the exception of CompositeLine) maintain their own
CoordinateSystem for the inlying Primitive and child composites, which
have their drawing origin at the top left corner of the BoundingBox.
All composites (with the exception of CompositeLine) have a size
and location defined by a BoundingBox.
The following list shows a few Repository composites taken from the Tensegrity Graph Demo Application.
A Composite containing a Rectangle:
![]() |
A Composite containing a Polygon:
![]() |
Tensegrity's “Little Man” Composite:
![]() |
The following diagram shows the interface hierarchy for composites, which will be explained in subsequent sections:
The class CompositeView and all subclasses of BaseCompositeContainer
will be discussed in detail later in this chapter, when interface CompositeGroup
is introduced.
The BaseComposite interface is an aggregated interface that combines
multiple roles and responsibilities. Apart from those roles which are inherited,
this interface defines new methods that deal with the containment, management and
drawing of Primitive objects to a Device.
It is very unlikely that you will create your own class that implements this
abstract interface. More likely, you will use classes that already implement
derived interfaces, such as Composite and CompositeLine,
two concrete specifications that define the behavioral contracts for rectangular
and line-based composites respectively.
This interface exists because client code does not always have to differentiate between rectangular and line composites, two types which exhibit deviant runtime behaviors.
A far more comfortable way to add multiple Primitive objects to this
container is by using an instance of interface GeometryDescriptor.
These named objects are instantiated from an XML file and can be more easily
defined and assembled. Moreover, a runtime object of this type provides the
configuration data for several Primitive objects anyways. So instead of
writing Java code to create the Primitive objects, you can define them
as a GeometryDescriptor written
in an XML file, load them at system startup and apply them by calling method
applyGeometry(GeometryDescriptor)
Similarly, the appearance of a BaseComposite can be set by applying a
StyleDescriptor. This object specifies how a Geometry is
represented on the screen in terms of line styles, area fill and more. You can
invoke the following method multiple times:
applyStyle(StyleDescriptor)
A BaseComposite must be representable on the screen. Therefore, it
uses a Renderer and a Device object to do so, as you can
see in the following draw methods:
void draw(Renderer, Device, Transform2D)
This method is called from the context the BaseComposite
lies within. The Transform2D given by
xform_context is the transformation needed to draw within
the context and is used to draw the BaseComposite.
void draw(Renderer, Device, int, int, int, int)
This method is called from outside when it is not possible to create a
transformation for the context the BaseComposite lies within
(This occurs for instance when using a coordinate system, that is not
able to create a linear transformation).
In this case the bounding box of the BaseComposite has to be
created from the context the BaseComposite lies within.
A BaseComposite is able to manage an InfoValue object.
This value object can be used in many different ways. Therefore, the following
constants are defined within the BaseComposite interface:
| Name | Description |
|---|---|
NON_INFOVALUE | No InfoValue is associated with the BaseComposite |
GENERAL_INFOVALUE | Defines an InfoValue that is used with no special purpose |
DELEGATE_INFOVALUE |
Defines an InfoValue for a delegate object.
Although delegation is sometimes used instead of generalization, it comes with
some problems because the methods of the delegation object do not override
the InfoValue.
If the InfoValue is marked with this constant, it is used instead of
the BaseComposite instance.
|
When implementing a BaseComposite, you have to support
the constants listed above.
Because the composite framework is under construction, the definition of more constants is likely.
The InfoValue is not deep-copied.
Object getInfoValue()
Returns the InfoValue assigned to the BaseComposite
or null if no InfoValue has been assigned.
It is recommended to read the documentation of this interface to get a
deeper understanding of how the InfoValue is used.
void setInfoValue(Object)
Sets the given Object as the new InfoValue of this
BaseComposite. Setting the
InfoValue with this method identifies it as a
GENERAL_INFOVALUE.
It is recommended to read the documentation of this interface to get a
deeper understanding of how a InfoValue can be used.
void setInfoValue(int, Object)
Sets the given Object as the new
InfoValue of this BaseComposite. The InfoValue
is identified by the constant that is given by
identifier.
It is recommended to read the documentation of this interface to get a
deeper understanding of how the InfoValue is used.
int getInfoValueIdentifier()
Returns the identifier that specifies the usage of the InfoValue.
The control over Primitive object containment is exposed to clients
with the following interface methods:
void addPrimitive(Primitive)
Adds the given Primitive to the composites list of
primitives.
PrimitiveIterator getPrimitiveIterator()
Returns a PrimitiveIterator that can be used to walk through
the BaseComposites list of Primitives.
void removeAllPrimitives()
Removes all Primitives from the BaseComposites
list of Primitives.
void removePrimitive(Primitive)
Removes the given Primitive from the composites list of
primitives.
A BaseComposite must support the EventMediator
protocol introduced by the top-level CompositeView container.
The method to do this is
void registerEventMediator(EventMediator)
We recommend that you do not register your own EventMediator
since this is done by the CompositeView, which automatically
contains all BaseComposite instances. Please read the class
documentation for CompositeView for more information.
The event-throwing mechanism of a BaseComposite can be turned on
and off. By invoking the enableEvents method and supplying the
specific event mask, you can control which events are thrown. A call to
disableEvents, on the other hand, suitably turns off the specified
events marked by the event mask parameter.
By default all events are turned on. To determine which events are enabled or
disabled, you can invoke the areEventsEnabled method. The event
masks are defined by an enumeration in this interface or in an interface that
extends this one. Event mask names always start with the EVENT_MASK
prefix. The methods a BaseComposite exposes to support the event
mechanism are:
void enableEvents(int)
void disableEvents(int)
int getEventMask()
boolean areEventsEnabled(int)
Because the BaseComposite interface extends the
AttributableOnSet interface, an instance will notify registered
listeners about changes to the managed Attribute objects.
As explained in the AttributableOnSet interface, notifications
can be turned on and off by using the appropriate methods.
When turning on or off the attribute change notification
of a BaseComposite, the attribute change
notification of all contained Primitive objects
is updated as well.
The Composite interface supplements the BaseComposite
behavior by managing its own CoordinateSystem. In doing so, the
coordinates of the contained Primitive objects remain independent
of any other CoordinateSystem used in a view, such as that of a
parent container or the top-level CompositeView.
Moreover, a Composite object has a location and a size within its
managed CoordinateSystem and is set by a BoundingBox.
This box defines the top-left corner and size of all inlying Primitive
objects managed by the Composite container.
Taken together, the CoordinateSystem and BoundingBox
of a Composite uniquely determine the transformation that is used
when drawing the contained Primitive objects.
Every object that implements Composite contains a separate CoordinateSystem.
This member can be accessed using the following methods:
CoordinateSystem getCoordinateSystem()
Returns the CoordianteSystem the composite uses.
void setCoordinateSystem(CoordinateSystem)
Sets the CoordinateSystem used by this
Composite to the given one. If you need a mutable version
you can set an instance of MutableCoordinateSystem.
The CoordinateSystem of a Composite that is defined in the
elements.xml file will be pooled and is therefore immutable.
See section
Coordinate Systems
of this chapter for more information on tis topic.
The following methods deal with the BoundingBox of a Composite:
void addToLocation(Size)
Adds the extensions given by sizes to the
Composites current location. These extensions should be
given in the coordinate system the Composite lies
within. Adding the extensions to the current location of the
Composite creates a MoveEvent if the extensions
are not zero. Notice that a call to this method has no effect if the
Composite is not movable.
void setBoundingBox(int, int, int, int)
Sets the composites bounding box to the values given by
left, right, top and
bottom. These values should be given in the coordinate
system the Composite lies within. Setting the bounding box
of a Composite creates a MoveEvent if the new
location of the bounding box is different from the current location and a
ResizeEvent if the new size of the bounding box is different
from the current size.
Notice that a call to this method has no effect to the position when the
Composite is not movable and no effect to the size when the
Composite is not resizable.
Coordinate getLocation()
Returns the current location of the Composite. The
components of the returned Coordinate are given in the
coordinate system the Composite lies within.
void setLocation(Coordinate)
Sets the locations of the Composite to the
Coordinate given by coordinate. The
components of the given Coordinate should be given in the
coordinate system the Composite lies within.
Setting the location of a Composite creates a
MoveEvent if the location is different from the current
location. Notice that a call to this method has no effect if the
Composite is not movable.
Size getMinimumSize()
Returns the minimum size for the Composite. While user
interaction takes place the size of the Composite is
compared against the minimum size and adjusted to this size if necessary.
void setMinimumSize(Size)
Sets the minimum size of the Composite to the value given
by size. While user interaction takes place the size of the
Composite is compared against the minimum size and adjusted
to this size if necessary.
Note: Currently the size of the Composite is only
compared against the minimum size while user interaction. Hence it is
possible to set a smaller size through the interface methods.
Size getSize()
Returns the current size of the Composite. The extensions of
the returned Size are given in the coordinate system
the Composite lies within.
void setSize(Size)
Sets the size of the Composite to the extensions given by
size. The extensions of the Size should be
given in the coordinate system the Composite lies within.
Setting the size of a Composite creates a
ResizeEvent if the size is different from the current size
of the Composite. Notice that a call to this methods has no
effect if the Composite is not resizable.
void layout(int, int, int, int)
Sets the Composites bounding box to the values given by
left, top, right and
bottom. These values should be given in the coordinate
system the Composite lies within.
Notice that a call to layout creates no events
and does not take care of the movable and resizable settings for the
Composite but takes the setting for the minimum size into
account.
The CoordinateSystem and BoundingBox of a Composite uniquely
determine the transformation that is used to draw the inlying
Primitive objects. You can access this Transform2D using the
following methods:
Transform2D getCoordinateSytemTransform(int)
Returns a Tranform2D that can be used to calculate the
transformation from or the transformation to this
Composite depending on the given type. The transformation is
definite determined by the CoordinateSystem and the
BoundingBox of this Composite.
Transform2D getTransform()
Returns the transformation the composite currently is transformed by.
void setTransform(Transform2D)
Sets the transform the composite is transformed by.
If you ever have a need to use these methods, please refer to section Composite Hierarchies in chapter Composite Utilities of this manual.
A Composite object fires events for every change that can happen
to it. In order to be notified about these events, you have to programmatically
register an object that implements the CompositeListener interface.
For more information about this interface, please take a closer look at the
documentation for CompositeListener.
Some of the events generated by a Composite are vetoable, which
means that the processing of an event can be programmatically stopped. In order
to veto an event, you have to register a VetoableCompositeListener.
For more information about this interface, please take a closer look at the
documentation for VetoableCompositeListener.
For more information about the event mechanism in the tensegrity frameworks,
please see interface EventListenerTag and
class EventMultiplexer.
The methods a Composite provides to support the event mechanism are:
void addCompositeListener(CompositeListener)
void removeCompositeListener(CompositeListener)
void addVetoableCompositeListener(VetoableCompositeListener)
void removeVetoableCompositeListener(VetoableCompositeListener)
The CompositeLine interface is used whenever a line is drawn or two
composites are connected with a link. This connecting edge is made up of a
CompositeLine - a Composite that holds a line and behaves
differently than normal composites.
The CompositeLine interface supplements the BaseComposite
behavior by managing a set of Coordinate objects that are used by
a parent CoordinateSystem.
To modify the coordinates of a CompositeLine, please use the following
methods:
void addCoordinate(Coordinate)
Adds the Coordinate given by coordinate to the
end of the CompositeLines actual coordinates.
Coordinate getCoordinateAt(int)
Returns the Coordinate at the index given by
index from the CompositeLines current
coordinates.
If the index is less than zero or larger than the actual
count of Coordinates an exception will be thrown.
int getCoordinateCount()
Returns the current number of Coordinates the
CompositeLine consist of.
Coordinate getStartingLocation()
Returns the starting Coordinate of the
CompositeLine.
The ending coordinate is the last Coordinate the
CompositeLine consists of (respectively the
CompositeLine starts at).
void setStartingLocation(Coordinate)
Sets the Coordinate given by coordinate as the
new starting coordinate of the CompositeLine. The starting
coordinate is the first Coordinate the
CompositeLine consists of respectively the
CompositeLine starts.
void insertCoordinate(int, Coordinate)
Inserts the Coordinate given by coordinate at
the index given by index. If the first index is less than
zero it is adjusted to zero. If the second index is larger than the
actual count of Coordinates it is adjusted to the actual
count of Coordinates
void removeCoordinate(int)
Removes the Coordinate at the index given by
index from the CompositeLines coordinates.
If the index is less than zero or larger than the actual count of
Coordinates an exception will be thrown.
This interface represents the top-level BaseCompositeContainer
in a tree of like-kind containers. Clients will reference this interface
type explicitly because it declares methods needed at the very top of the
hierarchy. Moreover, a CompositeView merges several basic interfaces
and offers special support for handling objects of type BaseComposite.
This interface is a mighty specification: the classes which implement it are
basically responsible for performing all drawing, event-handling, selection and
notification tasks necessary for a top-level view container.
It should come as no surprise, then, that this interface is merged into the interface
specification for VisualGraphView. This allows clients to speak to a
top-level VisualGraph as if it were a CompositeView
by nature.
A CompositeView will draw the following objects in the order
specified:
Contained Primitive instances
Contained BaseComposite instances
Selected BaseComposite instances
Contained BaseComposite indicators
void draw(Renderer, Device, Transform2D)
Draws the Primitives and Composites added to
the CompositeView with the Renderer given by
renderer onto the Device given by
device. This method draws everything that is contained in
this CompositeView. If drawing of a combination of
particular items inside this instance is required use the method
draw(Renderer, Device, Transform2D, int) and specify a bitmask
composed of the desired drawing flags.
void draw(Renderer, Device, Transform2D, int)
Draws the Primitives and Composites added to
the CompositeView with the Renderer
given by renderer onto the Device given by
device.
A CompositeView supports user interaction by extending the
InteractionProvider interface.
In order to plug additional interaction behavior into an instance, you must
register a CompositeInteractionHandler that is invoked by the
CompositeView whenever some form of user interaction takes place.
The table below gives information about how method calls are delegated.
Method of class CompositeView | Invoked method of class CompositeInteractionHandler |
|---|---|
| startActionAt | startActionAt |
| stopActionAt | stopActionAt |
| actAt | preActAt |
| actAt | actAt |
For more information, please refer to the JavaDoc of interface
CompositeInteractionHandler.
Because it is possible that the view area of CompositeView is
much larger than the drawing area, scrollbars are usually needed to make the
entire view area visible to the user. Since scrollbars cannot be attached to
a CompositeView, the relevant scroll information can nevertheless
be provided to an object that requires it. These are the methods to call:
int getHorizontalScrollbarValues()
Calculates and returns the values needed to setup a horizontal scrollbar. the values are as follows:
minimum value
maximum value
current value
current extend
The indices of the values are defined by the constants listed below.
int getVerticalScrollbarValues()
Calculates and returns the values needed to setup a vertical scrollbar. The values are as follows:
minimum value
maximum value
current value
current extend
The indices of the values are defined by the constants listed below.
Additionally, this interface specifies that Primitive objects can be
used to draw a title and other background graphics, objects which should not respond
to mouse events. In other words, these primitives should only be used for the views
static adornment. The methods to manage these Primitive
objects are:
void addPrimitive(Primitive)
Adds the given Primitive to the elements of the
CompositeView>.
void removePrimitive(Primitive)
Removes the given Primitive from
the elements of the CompositeView.
Two methods deal with the layered positioning of contained BaseComposite
instances:
void toBack(BaseComposite)
Sends the given composite to the back of the drawing order.
void toFront(BaseComposite)
Sends the given composite to the front of the drawing order.
A CompositeView has both a CoordinateSystem and its own
BoundingBox which allows it to scale and position all child composites
contained below it. The relevant methods are:
Boundary getBoundingBox()
Returns the current bounding box of the CompositeView.
void setBoundingBox(int, int, int, int)
Sets the bounding box of the CompositeView to the values
given by left, top, right and
bottom,
In addition to its own CoordinateSystem, a CompositeView
uses a second instance, which it calls the "reference coordinate system."
This CoordinateSystem is used to define how much of the view area
should be visible within the viewport of the CompositeView. The
following method is used to retrieve this CoordinateSystem.
CoordinateSystem getReferenceCoordinateSystem()
Returns the CoordinateSystem the CompositeView
currently uses as reference for the viewport and such.
A CompositeView also supports different
modes which can be externally configured.
int MODE_MASK_ALL
Indicating that all modes are set.
int MODE_MASK_CONTEXT_MENU_ALLOWED
Indicating that the context menu is enabled / disabled.
int MODE_MASK_EDITING_ALLOWED
Indicating that the editing of inlying BaseComposite objects is allowed.
int MODE_MASK_NONE
Indicating that no mode is set.
int MODE_MASK_SELECTION_ALLOWED
Indicating that selections of inlying Composite respectively
Primitive objects are allowed.
int MODE_MASK_SELECTIONBOX
Indicating the mode mask for rectangular selections.
These modes can be manipulated using the following methods:
boolean areEventsEnabled(int)
Returns true, if events of the given eventmask are enabled.
void disableEvents(int)
Disables all of the events specified in the given eventmask.
void enableEvents(int)
Enables all of the events specified in the given eventmask.
void enableMode(int)
Enables the mode specified by the given modemask
int getEventmask()
Returns the current eventmask.
int getModemask()
Returns the current setting for the mode the CompositeView
work in. The returned value will be in the following list:
MODE_MASK_EDITING_ALLOWED
MODE_MASK_CONTEXT_MENU_ALLOWED
MODE_MASK_SELECTION_ALLOWED
boolean isModeEnabled(int)
Returns a boolean that indicates whether the mode specified
by modemask is enabled or not.
As a container of BaseComposite objects, a CompositeView
will mediate all occurring events propagated by objects of type
Composite, CompositeLine, CompositeGroup
and, of course, its own type (CompositeView).
To be informed about these events, one must add an EventMediatorListener
or a VetoableEventMediatorListener to a CompositeView
instance.
The following interfaces can provide you with more information about the event
mediator mechanism:
EventMediator,
EventMediatorListener,
VetoableEventMediator,
VetoableEventMediatorListener.
The relevant event mediation methods are:
void addEventMediatorListener(EventMediatorListener)
void removeEventMediatorListener(EventMediatorListener)
void addVetoableEventMediatorListener(VetoableEventMediatorListener)
void removeVetoableEventMediatorListener(VetoableEventMediatorListener)
Events are delivered in the following order:
VetoableCompositeLineListener,
VetoableCompositeListener,
VetoableCompositeGroupListener
VetoableEventMediatorListener
CompositeLineListener,
CompositeListener,
CompositeGroupListener
EventMediatorListener
A CompositeView object fires events for every conceivable change
that can happen to it. In order to be notified about the occurrence of these
events, an object which implements the
CompositeViewListener interface
must be registered with an instance of this class.
Some of the events fired by a CompositeView may be vetoed, which
means that the processing of an event can be stopped. To veto an event, one must
register a VetoableCompositeViewListener
with an instance of this class.
The event-throwing mechanism of a CompositeView can be turned on
and off. By invoking the enableEvents method and supplying the
specific event mask, you can control which events are thrown. A call to
disableEvents, on the other hand, suitably turns off the specified
events marked by the event mask parameter.
By default all events are turned on. To determine which events are enabled or
disabled, you can invoke the areEventsEnabled method. The event
masks are defined by an enumeration in this interface or in an interface that
extends this one. Event mask names always start with the EVENT_MASK
prefix. The methods a CompositeView exposes to support the event
mechanism are:
void addCompositeViewListener(CompositeViewListener)
Adds the given CompositeViewListener to the
CompositeViews internal structure, such that the listener is
informed about every event that occurs.
void addVetoableCompositeViewListener(VetoableCompositeViewListener)
Adds the given VetoableCompositeViewListener to the
CompositeViews internal structure, such that the listener is
informed about every event that occurs.
void removeCompositeViewListener(CompositeViewListener)
Removes the given CompositeViewListener from the
CompositeViews internal structure, such that the listener is
no longer informed about occurring events.
void removeVetoableCompositeViewListener(VetoableCompositeViewListener)
Removes the given VetoableCompositeViewListener from the
CompositeViews internal structure, such that the listener is
no longer informed about occurring events.
Composite objects hold their own CoordinateSystem, which is nested within
the CoordinateSystem of a parent CompositeGroup or CompositeView. The
BoundingBox of a Composite, moreover, defines its size and location and
should be specified using the CoordinateSystem of its parent view.
Imagine a scenario with the following components:
A view with an x-axis going from 0 to 7000 and a y-axis of the same length.
Composite one:
CoordinateSystem: x-Axis from 0 to 2000, y-Axis from 0 to 2000
BoundingBox: left-top: (1000, 1000), right-bottom: (2000, 4000)
Composite two:
CoordinateSystem: x-Axis from 0 to 2000, y-Axis from 0 to 2000
BoundingBox: left-top: (5000, 5000), right-bottom: (6500, 6500)
A visualization of this arrangement looks as follows:
Note the CoordinateSystem of the second Composite. Although it has the
same maximum values as the first, the Composite does not have the same
size in the view because its size is determined by its BoundingBox. The
layout of Primitive objects in a Composite, however, is done by using
the CoordinateSystem of a Composite.
Now let us see how to create a Composite programmatically.
First of all, we need to create a CompositeView:
// Get a CompositeFactory instance CompositeFactory factory= CompositeFactory.newInstance(); // Create a view with the given CoordinateSystem Composite CompositeView compositeView= factory.newCompositeView();
As you can see, we have chosen to use MutableScale and MutableCoordinateSystem
objects so that we can change the minimum and maximum values later.
Now lets create a new Composite. In the following examples we will create
two of them:
CoordinateSystem coordSystem1=
new MutableCoordinateSystem(
new DefaultMutableScaleLinear(0, 2000),
new DefaultMutableScaleLinear(0, 2000));
Composite composite1= factory.newComposite(coordSystem1);
composite1.setBoundingBox(1000, 4000, 1000, 3500);
The creation of the second Composite is equivalent:
CoordinateSystem coordSystem2=
new MutableCoordinateSystem(
new DefaultMutableScaleLinear(0, 2000),
new DefaultMutableScaleLinear(0, 2000));
Composite composite2= factory.newComposite(coordSystem2);
composite2.setBoundingBox(5000, 6500, 5000, 6500);
Now we can add the two newly-created Composite instances to the view by simply
calling its addComposite method:
compositeView.addComposite(composite1); compositeView.addComposite(composite2);
Although the above code is not ready for use, it should give you a basic
understanding how to deal with the CoordinateSystem and BoundingBox
of a Composite.
This example[4]
is provided to get you used to the basic concepts of the CompositeAPI. We will have a
look at some real world examples in later sections.
Most often, you will use xml-based element definitions to specify a repository
from which Composite objects will be automatically created. However, if the
capabilites of these elements are not sufficient for your application, you have
the option to implement your own Composite class.
© 2004, 2005 Tensegrity Software GmbH