Creating a link shape policy with up to two bends

The example creates 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 prohibits the creation of more than 2 bends.
fwadv_linkshape5.gif
Link shape policy for 0, 1 or 2 bends
In principle, this link shape policy is a combination of the functionality of the classes IlvOneLinkImage and IlvDoubleLinkImage (see Links 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 the new link shape policy because it defines all the methods of the interface IlvLinkShapePolicy as empty methods.
public class MyLinkShapePolicy
    extends IlvAbstractLinkShapePolicy
{
    ...
}
You can concentrate on the few methods that you need to override.
To create your link shape policy:
  1. Create an auxiliary method that reshapes the link according to the policy. Depending on the number of bends and the horizontal and vertical distance, you can 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 on a link, or when a link is added to the grapher, you have to ensure that the link shape is correct. Call the method verifyLinkPoints inside the appropriate callback methods of the link shape policy.
    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, you must call 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);
    }
    
  3. When you set 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);
    }
    
  4. 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;
    }
    
  5. When bends are inserted or removed, the link policy must reshape the link so that it has the desired shape 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);
    }
    
  6. 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);
    }
    
  7. 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);
    }
    
  8. The link shape policy defines the positions of the bends completely. Therefore the policy forbids modification of the positions of the bends; that is, you are 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 on a link as follows:
link.setLinkShapePolicy(new MyLinkShapePolicy());
Note
For simplicity, the 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.