For experts: implementing your own label descriptors

The Annealing Label Layout is extensible. The point label descriptor and the polyline label descriptor are designed to cover the majority of the cases. In rare situations, you may want to implement your own label descriptor by subclassing the base class IlvAnnealingLabelDescriptor. This section describes the necessary steps.
A label descriptor basically specifies the path where the top-left corner of a label can be placed. For simplification, it considers the path rolled out such that the path has only one dimension. If the path is known, the precise label position can be specified by just one value: the path location. The Annealing Label Layout proposes different path locations during the layout; however, it does not know what the path looks like. The task of the label descriptor is to translate the path location into concrete (x, y) coordinates of the label.
As an example, we want to create a label descriptor that can place labels precisely at a triangular obstacle. We could use the point label descriptor as an approximation, but it does not place the labels precisely at a triangular shape.
In the following figure, the upper diagram shows the path around the triangle (the dashed red and blue line). Below, you can see the same path rolled out in one dimension. The Annealing Label Layout may ask the label descriptor to place the label at position 1 to 8. For the Annealing Label Layout, these positions are just numbers between 0 and maxPathLocation . The task of the label descriptor is to translate these numbers into the correct positions as shown in the upper part of the figure.
Diagram
illustrating path locations around a triangle shape
Path locations at a triangle label descriptor
The base class IlvAnnealingLabelDescriptor. has two protected data members:
  • actPathLocation is the current path location of the label.
  • maxPathLocation is the maximum value of the path location.
To create a new label descriptor, you need to implement a method that initializes the path constructor at the beginning of layout. You should calculate the maximum path location maxPathLocation and initialize the actPathLocation here. The method is called only once during layout:
void initialize(IlvLabelingModel labelingModel)  
In the previous figure, the maximum path location for an equilateral triangle is:
3 * sidelength + 2 * labelwidth + 2  * labelheight 
At each iteration step, the layout calls the method setPosition and provides an actual value for the path location. The method setPosition should store the value into actPathLocation and translate the path location into appropriate (x, y) coordinates. Then it should call the predefined method updatePosition(x, y) with these coordinates:
public void setPosition(double pathLocation, float distFromPath)
{
    float x, y;
    // make sure the position is between 0 and max
    while (pathLocation > maxPathLocation)
        pathLocation -= maxPathLocation;
    while (pathLocation < 0)
        pathLocation += maxPathLocation;
    // store the actual position
    actPathLocation = pathLocation;
    // translate the path location into (x, y)
    if (pathLocation < labelwidth + sidelength) {
        x = (float)pathLocation;
        y = triangleBottom;
    } else if (pathLocation < labelwidth + labelheight + sidelength) {
        x = labelwidth + sidelength;
        y = triangleBottom - (float)pathLocation + labelwidth + sidelength;
    } else if ... (other cases) ...
        ...
    // finally, update the internal data structures
    updatePosition(x, y);
} 
The label may have a preferred position at the triangle. The Annealing Layout checks a location close to the preferred position from time to time. You should implement the following method to return the preferred path location:
double getPreferredPathLocation()  
Furthermore, you should implement a strategy on how to come close to the preferred location. Towards the end of layout, the algorithm calls the method:
setTowardsPreferredPosition(pathLocation, dist, i, maxI)  
to perform a sequence of steps that shift the label from the current position closer to the preferred position.
with i from 1 to maxI . Implement the method so that at each step you calculate a path location closer to the preferred location. When i is maxI , it should be exactly at the preferred location. You can call setPosition to move the label to the preferred (x, y) position. For instance:
public void setTowardsPreferredPosition(
                double pathLocation, float dist, int i, int maxI)
{
    double offset = pathLocation - getPreferredPathLocation();
    double newLocation = pathLocation - i * offset / maxI;
    setPosition(newLocation, dist);
}
These methods take the distance parameter in addition to the path location. This is the distance from the path. If the label must always be on the path, you can assume this distance is 0. Set it to a different value only if your label descriptor allows the label to have a variable offset from the path.