How to implement a custom label model

Tips & Tricks

Summary

This article describes how to create a custom label model.

Description

In some cases the built in label models are not sufficient to produce the desired result, e.g. when a node label should fit into an irregular shape. This article describes how to create a custom label model.

The following example shows how to create a label model which places the label in the center of the node and sizes the label to 2/3 of the node bounds.

A custom label model has to implement the interface ILabelModel. This interface has four methods to implement:

We see that in addition to the model we also need to implement a model parameter. This parameter can hold some extra information which is specific for the label. In the standard yFiles Flex label models, this is the relative position. Example: ExteriorLabelModel.south is a label model parameter for the model ExteriorLabelModel with the label positioned below the node.

The label model parameter has to implement the interface ILabelModelParameter. This interface has three methods to implement:

Implementation of the model parameter

  • The model paramter needs to know its model. Thus, we pass the model in the constructor and store it in a field. The model getter returns this field.
  • The model is the only parameter which we need to clone.
  • This parameter supports all node labels, so our supports method returns true if the label's owner is a node.

package demo.test
{
  import com.yworks.graph.model.ILabel;
  import com.yworks.graph.model.ILabelModel;
  import com.yworks.graph.model.ILabelModelParameter;
  import com.yworks.graph.model.INode;

  public class ExampleLabelModelParameter implements ILabelModelParameter
  {
    private var _model:ExampleLabelModel;

    public function ExampleLabelModelParameter(model:ExampleLabelModel)
    {
      this._model = model;
    }

    public function clone():Object
    {
      return new ExampleLabelModelParameter(_model);
    }

    public function get model():ILabelModel
    {
      return this._model;
    }

    public function supports(label:ILabel):Boolean
    {
      return label != null && label.owner is INode;
    }

  }
}

Implementation of the model

  • createDefaultParameter() returns an instance of the only parameter
  • getGeometry() uses the label owner's layout to calculate the bounding rectangle for the label which should be centered and have 2/3 of the node's size. If the calculation is not possible (there is no label or the label's owner is not a node), an empty rectangle will be returned.
  • We don't need the lookup at the moment, so it returns null
  • We don't need the context either, so it returns com.yworks.support.Lookups.EMPTY.
package demo.test { import com.yworks.canvas.geom.IOrientedRectangle; import com.yworks.canvas.geom.IRectangle; import com.yworks.canvas.geom.OrientedRectangle; import com.yworks.graph.model.ILabel; import com.yworks.graph.model.ILabelModel; import com.yworks.graph.model.ILabelModelParameter; import com.yworks.graph.model.INode; import com.yworks.support.ILookup; import com.yworks.support.Lookups; public class ExampleLabelModel implements ILabelModel { public function lookup(type:Class):Object { return null; } public function getGeometry(modelParameter:ILabelModelParameter, label:ILabel):IOrientedRectangle { if (label != null) { var node:INode = label.owner as INode; if (node != null) { var layout:IRectangle = node.layout; // size is 2/3 of the node size var width:Number = layout.width * 0.6; var height:Number = layout.height * 0.6; // place the rectangle centered var x:Number = layout.x + (layout.width - width)/2; // note that in an oriented rectangle // x,y represent its *lower* left corner var y:Number = layout.y + (layout.height + height)/2; return OrientedRectangle.create(x, y, width, height, 0, -1); } } return OrientedRectangle.EMPTY; } public function createDefaultParameter():ILabelModelParameter { return new ExampleLabelModelParameter(this); } public function getContext(label:ILabel, parameter:ILabelModelParameter):ILookup { return Lookups.EMPTY; } } }

Advanced features

The lookup() and getContext() methods of a label model are queried by the yFiles Flex library to provide additional functionality.

Currently lookup() is queried for the following interfaces (for details see the API docs):

InterfaceNeeded for
ILabelModelParameterProvider Asked for possible label positions when a label is moved by the user.
ILabelModelParameterFinder Asked for the best label model parameter for a given label layout (lets the label "snap" to a possible candidate).
ISerializer A serializer which can handle this label model parameter (note that the parameters are serialized, not the model. Also note that a matching deserializer has to be registered with the IOHandler).
INodeInsetsProvider Takes the label into account for node insets.

getContext() is queried to get a lookup for the following interface (for details see the API docs):

InterfaceNeeded for
ILabelCandidateDescriptor Describes some properties of a candidate label model parameter that may be used by automatic labeling algorithms.

Example: A label model which places the label centered inside the node

The example label model VerticalCenterStretchLabelModel, which can be downloaded as resource, can be used to place a label centered inside a node. It overcomes the following shortcomings of the build in label models:

  • InteriorLabelModel.center places the label at the center of the node. A long label text, however, will exceed the node's bounds.
  • InteriorStretchLabelModel.center uses the node's bounds as label bounds. Thus, the label will not exceed the node's bounds. The text, however, will start at the top left corner of the node. Thus, the label text does not appear centered.

The VerticalCenterStretchLabelModel uses the node's bounds as maximum width and height. If necessary, it breaks the lines and word wraps the text. The label's actual bounds are not larger than necessary. Thus, the label doesn't exceed the node's bounds and appears centered within the node.

The label model parameter can be obtained using the createDefaultParameter() method. To center the text within the label's bounds one has to set the text format's align parameter to "center". Example usage:

// get a model parameter
graphCanvas.graph.defaultNodeLabelModelParameter = new VerticalCenterStretchLabelModel().createDefaultParameter();
// center the text within the label
var style:SimpleLabelStyle = new SimpleLabelStyle();
style.textFormat = FontManager.INSTANCE.getDefaultTextFormat();
style.textFormat.align = TextFormatAlign.CENTER;
graphCanvas.graph.defaultNodeLabelStyle = style;

Resources

Categories this article belongs to:
yFiles FLEX > Displaying and Editing Graphs > Styles-related Features
Applies to:
yFiles FLEX: 1.4, 1.5, 1.6, 1.7, 1.8
Keywords:
ILabel - ILabelModel - ILabelModelParameter - label - label model