Advanced Features > Link Shape Policies > Defining Your Own Link Shape Policy > Example of a User-Defined Link Shape Policy

This section uses an example to show how you can implement your own link shape policy. You will create a link shape policy that allows links to have 0, 1 or 2 bend points. With 1 or 2 bends, the link will have an orthogonal shape. The link shape policy will prohibit the creation of more than 2 bends.

images/fwadv_linkshapea5.gif

Figure 2.3 Link Shape Policy for 0, 1 or 2 bends

In principle, this link shape policy is a combination of the functionality of IlvOneLinkImage and IlvDoubleLinkImage (see Basic Grapher Link Class of The Essential JViews Framework). However, using a link shape policy is more flexible, because the link shape policy can be enabled and disabled on the link, and can change from a 1-bend image to a 2-bend image.

The class IlvAbstractLinkShapePolicy is a suitable base class for our new link shape policy because it defines all the methods of the interface IlvLinkShapePolicy as empty methods.

Hence, you can concentrate on the few methods that you need to override:

public class MyLinkShapePolicy
    extends IlvAbstractLinkShapePolicy
{
    ...
}

  1. First, you need an auxiliary method that reshapes the link according to the policy. Depending on the number of bends and the horizontal and vertical distance, you decide which shape the link should have. For instance:
    private void verifyLinkPoints(IlvLinkImage link)
    {
        if (link == null)
          return;
    
        IlvPoint[] pts = link.getLinkPoints(null);
        int n = pts.length;
        if (n <= 2) return;
    
        IlvRect fromrect = link.getFromBoundingBox(null);
        IlvRect torect = link.getToBoundingBox(null);
        float fx = fromrect.x + 0.5f * fromrect.width;
        float fy = fromrect.y + 0.5f * fromrect.height;
        float tx = torect.x + 0.5f * torect.width;
        float ty = torect.y + 0.5f * torect.height;
        float dx = (fx < tx ? 0.5f : -0.5f);
        float dy = (fy < ty ? 0.5f : -0.5f);
    
        if (n == 3) {
          link.movePoint(0, fx + dx * fromrect.width, fy, null);
          link.movePoint(1, tx, fy, null);
          link.movePoint(2, tx, ty - dy * torect.height, null);
          return;
        }
    
        if (Math.abs(fx - tx) > Math.abs(fy - ty)) {
          float middleX = 0.5f * (fx + tx +
                            dx * (fromrect.width - torect.width));
          pts[1].move(middleX, fy);
          pts[2].move(middleX, ty);
          link.movePoint(0, fx + dx * fromrect.width, fy, null);
          link.movePoint(n-1, tx - dx * torect.width, ty, null);
        } else {
          float middleY = 0.5f * (fy + ty +
                            dy * (fromrect.height - torect.height));
          pts[1].move(fx, middleY);
          pts[2].move(tx, middleY);
          link.movePoint(0, fx, fy + dy * fromrect.height, null);
          link.movePoint(n-1, tx, ty - dy * torect.height, null);
        }
    
        link.setIntermediateLinkPoints(pts, 1, 2);
    }
    
  2. When the link policy is set to a link, or when a link is added to the grapher, you have to ensure that the link shape is correct. To do so, you simply call the method verifyLinkPoints inside these corresponding callback methods of the link shape policy.
  3. The callback method afterAdd is called when the link is already in the grapher. Since the link shape policy will reshape the link and might change the bounding box of the link, it has to be performed via the method applyToObject:
    public void afterAdd(IlvLinkImage link)
    {
        if (link.getGraphicBag() != null) {
            link.getGraphicBag().applyToObject(link,
                new IlvApplyObject() {
                    public void apply(IlvGraphic obj, Object arg) {
                        verifyLinkPoints((IlvLinkImage)obj);
                    }
                }, null, true);
            }
            else verifyLinkPoints(link);
        super.afterAdd(link);
    }
    
  4. When setting the link shape policy, you can rely on the user to call applyToObject, therefore you do not need to use this method again inside the link shape policy. Hence, the code of the method onInstall is simpler:
    public void onInstall(IlvLinkImage link)
    {
        verifyLinkPoints(link);
        super.onInstall(link);
    }
    
  5. The link shape policy allows the link to have 0, 1, or 2 bends. Since the two end points of the link are counted in the link cardinality, you just have to forbid insertion of more than four points, so that there will never be more than two bends:
    public boolean allowInsertPoint(IlvLinkImage link,
                                    int index,
                                    float x, float y,
                                    IlvTransformer t)
    {
        int n = link.getPointsCardinal();
        if (n >= 4) return false;
        return true;
    }
    
  6. When bends are inserted or removed, the link policy must reshape the link so that it has the shape desired for this number of bends. Therefore, you override the methods afterInsertPoint, afterRemovePoint, and afterSetIntermediateLinkPoints to call verifyLinkPoints. Note that all these methods are always called inside the method applyToObject. Thus, the code can omit an additional call to applyToObject similar to onInstall:
    public void afterInsertPoint(IlvLinkImage link, int index, 
                                              IlvTransformer t)
    {
        verifyLinkPoints(link);
        super.afterInsertPoint(link, index, t);
    }
    
    public void afterRemovePoint(IlvLinkImage link, int index,
                                             IlvTransformer t)
    {
        verifyLinkPoints(link);
        super.afterRemovePoint(link, index, t);
    }
    
    public void afterSetIntermediateLinkPoints(IlvLinkImage link)
    {
        verifyLinkPoints(link);
        super.afterSetIntermediateLinkPoints(link);
    }
    
  7. Changing the transformation of a polyline link image usually also modifies the bends of the link. Therefore the link points need to be verified here as well:
    public void afterApplyTransform(IlvLinkImage link, IlvTransformer t)
    {
        verifyLinkPoints(link);
        super.afterApplyTransform(link, t);
    }
    
  8. When an end node of the link is moved, the 1 or 2 bends of the link must adjust accordingly. Therefore it is necessary to override the corresponding callback methods of the link shape policy:
    public void afterFromNodeMoved(IlvLinkImage link)
    {
        verifyLinkPoints(link);
        super.afterFromNodeMoved(link);
    }
    
    public void afterToNodeMoved(IlvLinkImage link)
    {
        verifyLinkPoints(link);
        super.afterToNodeMoved(link);
    }
    
  9. Finally, the link shape policy defines the positions of the bends completely. Therefore the policy forbids modification of the positions of the bends; that is, it is not allowed to move a point:
    public boolean allowMovePoint(IlvLinkImage link,
                                  int index,
                                  float x, float y,
                                  IlvTransformer t)
    {
        return false;
    }
    

The callback method afterMovePoint of the link shape policy could be used to adjust the link shape after moving points. However, since this link shape policy does not allow point movement, the method afterMovePoint need not be overridden because point movements will never be executed.

The new link shape policy works for all subclasses of IlvPolicyAwareLinkImage. It can be set to a link in the following way:

link.setLinkShapePolicy(new MyLinkShapePolicy());

Note
For simplicity, this example class MyLinkShapePolicy is designed for zoomable, rectangular end nodes that have no link connector or a free link connector (IlvFreeLinkConnector). If this is not the case, the method verifyLinkPoints needs to be adjusted to analyze the current transformer, the shape of the end nodes, and the link connector.