This page describes how automatic layout can be configured for a given application. This includes how layout options can be set on graph elements, and how they are applied by KIML during the layout process. After having read this, you should be able to answer the following questions:

This page does not list the available layout options, and neither does it explain any of them. You can find a list of layout options provided by KIML over here.

 

Contents

Layout Options and What They Are Good For

Even the most basic layout algorithm provides some settings for you to play with. This might be something as simple as the space left between different nodes, or something as complex as changing how node labels are placed and how that influences the size of each node. Each such setting must be registered with KIML as a layout option, and each algorithm must specify exactly which of these options it supports. Registering a layout option is done through one of KIML's extension points and can look like this:

<extension point="de.cau.cs.kieler.kiml.layoutProviders">
    <layoutOption
        id="de.cau.cs.kieler.nodeLabelPlacement"
        name="Node Label Placement"
        description="Hints for where node labels are to be placed; if empty, the node label's position is not modified."
        advanced="true"
        appliesTo="nodes"
        type="enumset"
        class="de.cau.cs.kieler.kiml.options.NodeLabelPlacement"
        default="">
    </layoutOption>
</extension>

Let's walk through the attributes available for layout options (not every available attribute appears in the example above):

The latter three attributes are used when a layout configuration is determined automatically, e.g. with an evolutionary algorithm. They are mainly meant for scientific experiments and can be ignored in most applications.

If a layout algorithm supports a particular layout option, it must tell KIML so. Here's an example:

<extension point="de.cau.cs.kieler.kiml.layoutProviders">
    <layoutAlgorithm ...>
        <knownOption
            option="de.cau.cs.kieler.borderSpacing"
            default="20">
        </knownOption>
    </layoutAlgorithm>
</extension>

This tells KIML that the defined layout algorithm supports the border spacing option. And even more, it overrides the default value declared by the layout option and sets it to 20.

The Layout Options Manager

By now, we have an idea of what layout options do and why they are important in the first place. However, we haven't looked at how layout options end up on KGraph elements yet. This is where the LayoutOptionsManager comes in.

After a diagram layout manager has finished turning a given diagram into its KGraph representation, the layout options manager is asked to enrich the KGraph elements with layout options. The option values can come from different sources: the user might have set some using the layout view; there might be some defaults for certain kinds of diagrams; or the programmer might have decided to attach some layout options to certain elements for just this one layout run. Whatever the source, the options manager is in charge of collecting all these layout option values and making sure they find their way to the correct KGraph element. To start off with a clean plate, it first makes sure there are no layout options attached to the KGraph elements. It then does two things: collect every eligible source of layout options, and transfer values of layout options to the associated KGraph elements. Sounds easy enough.

The question remains how the layout options sources work. Each source is represented by a class that implements the ILayoutConfig interface, called a layout configurator. KIML currently provides the following layout configurators, each representing a particular source of layout options, listed here in order of increasing priority:

The options manager collects all available and applicable layout configurators and sorts them by priority. For every graph element, each configurator is asked to provide layout options, starting with the one with lowest priority and working through the priority chain. Hereby configurators with higher priority are able to override values set by those with lower priority.

A Few Details on Layout Configurators

What we just learned is a bit of a simplification of what happens. Before we look at the details, let's take a look at the methods each layout configurator provides:

public interface ILayoutConfig {
    int getPriority();
    Object getOptionValue(LayoutOptionData optionData, LayoutContext context);
    Collection<IProperty<?>> getAffectedOptions(LayoutContext context);
    Object getContextValue(IProperty<?> property, LayoutContext context);
} 

It is not hard to guess what getPriority() does: it returns the priority a given layout configuration has. If two layout configurations set a layout option to different values on a given graph element, the value set by the configuration with higher priority wins.

The interface discerns between option values and context values. Option values are what we have been talking about all the time, values assigned to layout options. Which particular values the configurator should apply depends on the given LayoutContext, which is a property holder with references to the diagram element currently in focus. For instance, the object representing an element in the diagram viewer is accessed with context.getProperty(LayoutContext.DIAGRAM_PART). Similarly, the corresponding KGraph element is mapped to the property LayoutContext.GRAPH_ELEM, and the domain model element is mapped to LayoutContext.DOMAIN_MODEL. Each configurator is free to put additional information into the context, caching it for faster access and enabling to communicate it to other configurators. getAffectedOptions(LayoutContext) should return a collection of layout options for which the configurator yields non-null values with respect to the given context. The options can be referenced either with LayoutOptionData instances obtained from the LayoutMetaDataService or with Property instances from the constants defined in LayoutOptions. The actual value for a layout option is queried with getOptionValue(LayoutOptionData, LayoutContext). The method getContextValue(IProperty, LayoutContext), in contrast, is used to obtain more detailed information on the given context. For instance, the context may contain a reference to an element of the diagram viewer; only a specialized configurator made for that diagram viewer knows how to extract a reference to the corresponding domain model element from the given diagram element, so it can encode this knowledge in getContextValue(…) by returning the domain model element when the given property corresponds to LayoutContext.DOMAIN_MODEL.

This may seem complicated, and it is, but the good news is that the vast majority of developers will not need to dig that deep into the layout configuration infrastructure. There are easier ways to specify configurations, as described in the following section.

Programmatically Setting Layout Options

So with all these layout configurators available, how do you actually go about setting layout options programmatically? Well, as always: it depends.


Write this section. This will be about when to use the different kinds of layout configurations, mainly SemanticLayoutConfig and VolatileLayoutConfig.