C

LayoutExecutor

Executes a ILayoutAlgorithm and optionally animates the transition to the calculated layout.
Inheritance Hierarchy

Remarks

This class is the preferred way to execute a layout on the current or main thread. The layout animation can be customized in various ways via this class' properties.

If no fine-grained control of the animation is required, the methods applyLayout and applyLayoutAnimated can be used instead.

For larger graphs and complicated layouts that have a greater execution time, blocking the main JavaScript thread of the browser can result in a poor user experience. In order to reduce the blocking time, a Web Worker or an external layout service process may be used. This can be implemented conveniently with the help of the LayoutExecutorAsync class, which is almost fully API compatible to this class, but requires a two-way communication setup between the main thread and the worker thread. It is thus recommended to start with using this approach and potentially blocking the main thread and only switch to the multi-threaded solution in a second step, if required. The code used for the solution, here, can be reused for the asynchronous solution, too.

See Also

Migrating from LayoutExecutor to LayoutExecutorAsync and when this should be done is explained in the section Migrating from Synchronous to Asynchronous Layout Calculation .

Developer's Guide

API

LayoutExecutorAsync, LayoutGraphAdapter, applyLayoutAnimated

Members

Show:

Constructors

Initializes a new instance of the LayoutExecutor class.
Use the start method to actually execute the task. This will disable animation and content bounds updates.

Parameters

graphComponent: GraphComponent
The control which will be animated and provides the IGraph instance.
layout: ILayoutAlgorithm
The ILayoutAlgorithm to use.
Initializes a new instance of the LayoutExecutor class.
The IGraph instance is required only when the graph being arranged differs from the one provided by the graphComponent. For example, this applies when using a FoldingManager instance and the layout algorithm needs to be applied to the master graph. Otherwise, the other constructor variant can be used.

Parameters

graphComponent: GraphComponent
The control which will be animated and provides the IGraph instance.
graph: IGraph
The graph to layout.
layout: ILayoutAlgorithm
The ILayoutAlgorithm to use.

Properties

Gets the layout graph adapter that is used by this instance to calculate the layout.
readonlyfinal

Property Value

The layout graph adapter.
Gets or sets the mapping of graph items to LayoutAnchoringPolicy values, specifying which part of the items should be used to anchor the graph during layout.

This property anchors the graph on an initial position based on either a single graph item or the alignment of the bounds of several items (but not the positions of the individual items).

The default LayoutAnchoringPolicy for all items is NONE, meaning items are not anchored unless explicitly specified.

This feature does not alter the layout calculated by the layout. It only moves the entire graph as a post-processing step. For use cases requiring an incremental layout, where the algorithm should only arrange some items while others remain unchanged (or change only slightly), please refer to the PartialLayout, the HierarchicalLayout (see mode fromSketchMode), or the OrganicLayout (see property scope).
Ports are not supported.
conversionfinal

Examples

Use the bounds of all items to anchor the graph, ensuring that the overall structure remains stable:

Use the bounds of all items as anchor
const layout = new HierarchicalLayout()
await new LayoutExecutor({
  graphComponent: graphComponent,
  layout: layout,
  anchoredItems: LayoutAnchoringPolicy.BOUNDS,
}).start()

Alternatively, anchor the graph using the location of a single item. In this example, the upper-left corner of a node is fixed. This is particularly useful when recalculating the layout for scenarios like expanding or collapsing a group node. To provide a seamless user experience, the group node itself remains in the same position, ensuring that the expand/collapse button stays directly under the mouse pointer:

Fixes the upper-left corner of a single node
const layout = new HierarchicalLayout()
await new LayoutExecutor({
  graphComponent: graphComponent,
  layout: layout,
  anchoredItems: (item) =>
    item === fixedNode
      ? LayoutAnchoringPolicy.LOWER_LEFT
      : LayoutAnchoringPolicy.NONE,
}).start()
Gets or sets a value indicating whether the viewport should be animated to the new bounds of the graph.

The result when this property is true after the animation is the same as calling fitGraphBounds. Setting this property to true and changing animationDuration to ZERO will disable the animation, but still change the viewport to the new graph bounds.

When the viewport should stay the same, the layout algorithms often have to be coerced to keep parts of the graph in the same location. This can be done by wrapping the layout algorithm in an instance of LayoutAnchoringStage.

The default value is true.

final

Property Value

true if the viewport should be animated; false otherwise.

See Also

Developer's Guide
Gets or sets the duration of the animation.
The default value is ZERO.
conversionfinal

Property Value

The duration of the animation. A value smaller than or equal to ZERO will prevent the animation from happening. Instead, the result is applied immediately. A positive value will effectively force the execution to be performed asynchronously.

See Also

Developer's Guide
Gets or sets the maximum runtime for the layout calculation before it is automatically canceled.
Exceeding this limit terminates the algorithm and raises an error immediately. This setting only affects the layout calculation time and does not include time that is needed for layout animation.
conversion

Property Value

the duration to wait before the layout calculation is canceled. Automatic termination will only occur for positive values.

Throws

Exception ({ name: 'ArgumentError' })
if the duration is negative

See Also

API
cancelDuration
Gets or sets a value indicating whether to respect the viewportLimiter of the GraphComponent of this instance.
The default value is true, but as updating the layout typically also updates the contentBounds, depending on the ViewportLimiter implementation and configuration, this could be set to false, instead.
final

Property Value

true if the viewportLimiter should be considered, otherwise false.
Gets or sets a comparison function that normalizes the order of the edges for the layout calculation to ensure the same order for multiple layout invocations.

Among other factors, the results produced by layout algorithms usually depend on the order of the nodes and edges within a graph. Unfortunately, useful operations such as hiding or unhiding elements from a graph or simply invoking layout algorithms on a graph will have the potential side effect of changing that order.

With this comparison it is possible to establish a predefined order of edges within a graph to avoid non-deterministic layout behavior.

final

See Also

API
nodeComparator
Gets the graph this instance is working on.
protectedreadonlyfinal

Property Value

The graph.
Gets the control this instance has been created for.
protectedreadonlyfinal

Property Value

The control.
Gets or sets a mapping that specifies how ILabels should be placed by the layout algorithm.

This setting only affects layout algorithms which support label placement. Also, if EdgeLabelPreferredPlacements are already defined for a label, all values other than KEEP_PARAMETER are ignored for that label.

Default is PREFER_MODEL.

conversionfinal

Examples

Maintain the current label positions as much as possible during layout:

Preserve current label placement
const layout = new HierarchicalLayout({
  edgeLabelPlacement: EdgeLabelPlacement.INTEGRATED,
})
await new LayoutExecutor({
  graphComponent: graphComponent,
  layout: layout,
  labelPlacementPolicies: LabelPlacementPolicy.PREFER_PARAMETER,
}).start()

Customize label placement individually for each label. In this example, the placement policy is determined by the type of the label's owner:

Custom label placement per owner type
const layout = new HierarchicalLayout({
  edgeLabelPlacement: EdgeLabelPlacement.INTEGRATED,
})
await new LayoutExecutor({
  graphComponent: graphComponent,
  layout: layout,
  labelPlacementPolicies: (label) =>
    label.owner instanceof INode
      ? LabelPlacementPolicy.PREFER_MODEL
      : LabelPlacementPolicy.PREFER_PARAMETER,
}).start()

See Also

Developer's Guide
API
EDGE_LABEL_PREFERRED_PLACEMENT_DATA_KEY, labelPlacementPolicies
Gets the ILayoutAlgorithm this instance is using.
protectedreadonlyfinal

Property Value

The layout.
Gets or sets the layout data that is applied when starting the executor.
This property can be reset at any time between runs and will be reused and reevaluated for each start
final
Gets or sets a comparison function that normalizes the order of the nodes for the layout calculation to ensure the same order for multiple layout invocations.

Among other factors, the results produced by layout algorithms usually depend on the order of the nodes and edges within a graph. Unfortunately, useful operations such as hiding or unhiding elements from a graph or simply invoking layout algorithms on a graph will have the potential side effect of changing that order.

With this comparison it is possible to establish a predefined order of nodes within a graph to avoid non-deterministic layout behavior.

final

See Also

API
edgeComparator
Gets or sets the mapping from ports to a policy that specifies how port locations should be adjusted after a layout has been calculated.

This can be useful if the port assignment calculated by the layout algorithm is insufficient.

Layout algorithms only consider rectangular nodes even though the actual shape of a node is, for example, circular. Hence, the ports are usually placed at the border of the nodes' bounds (except for some layout algorithms that produce straight-line edge routes and place the ports at the nodes' center).

Based on this setting the edges will be shortened or lengthened in a way that their sourcePorts and targetPorts will be placed on the node's outline.

The default is a constant ItemMapping<TItem, TValue> returning LENGTHEN for all ports.

The coordinates of a port will not be changed if the port is associated with a fixed port candidate, e.g. by setting portPlacementPolicies to KEEP_PARAMETER for the port.
conversionfinal

Examples

Automatically lengthen or shorten edges at all ports if the port is not located on the owner's outline:

Adjust edges at ports automatically
const layout = new HierarchicalLayout()
await new LayoutExecutor({
  graphComponent: graphComponent,
  layout: layout,
  portAdjustmentPolicies: PortAdjustmentPolicy.ALWAYS,
}).start()

Customize edge adjustments individually for each port. In this example, the policy is determined by the type of the port's owner:

Custom edge adjustment per port owner
const layout = new HierarchicalLayout()
await new LayoutExecutor({
  graphComponent: graphComponent,
  layout: layout,
  portAdjustmentPolicies: (port) =>
    port.owner instanceof INode
      ? PortAdjustmentPolicy.SHORTEN
      : PortAdjustmentPolicy.LENGTHEN,
}).start()

See Also

Developer's Guide
API
portAdjustmentPolicies
Gets or sets how ILabels at IPorts should be treated by the layout algorithm.
This mapping is queried for each ILabel at an IPort and should return a PortLabelPolicy value indicating how the label should appear to the layout algorithms. The default is a constant ItemMapping<TItem, TValue> returning NODE_LABEL for all labels.
conversionfinal

Examples

Treat all port labels as edge labels during layout:

Treat port labels as edge labels
const layout = new HierarchicalLayout()
await new LayoutExecutor({
  graphComponent: graphComponent,
  layout: layout,
  portLabelPolicies: PortLabelPolicy.EDGE_LABEL,
}).start()

Customize the handling of port labels individually. In this example, the policy is determined by the type of the port's owner:

Custom port label policy per owner type
const layout = new HierarchicalLayout()
await new LayoutExecutor({
  graphComponent: graphComponent,
  layout: layout,
  portLabelPolicies: (label) =>
    label.owner instanceof IPort && label.owner.owner instanceof INode
      ? PortLabelPolicy.NODE_LABEL
      : PortLabelPolicy.EDGE_LABEL,
}).start()

See Also

Developer's Guide
API
PortLabelPolicy, portLabelPolicies
Gets or sets a mapping that specifies how IPorts should be placed by the layout algorithm.

This setting only affects layout algorithms which support PortCandidates. Also, if PortCandidates are already defined for an edge, they override the current port positions.

Default is PREFER_MODEL.

conversionfinal

Examples

Maintain the current port locations as much as possible during layout:

Preserve current port locations
const layout = new HierarchicalLayout()
await new LayoutExecutor({
  graphComponent: graphComponent,
  layout: layout,
  portPlacementPolicies: PortPlacementPolicy.KEEP_SIDE,
}).start()

Customize port locations individually for each port. In this example, the placement policy is determined by the type of the port's owner:

Custom port location per owner type
const layout = new HierarchicalLayout()
await new LayoutExecutor({
  graphComponent: graphComponent,
  layout: layout,
  portPlacementPolicies: (port) =>
    port.owner instanceof INode
      ? PortPlacementPolicy.KEEP_PARAMETER
      : PortPlacementPolicy.PREFER_MODEL,
}).start()

See Also

Developer's Guide
API
SOURCE_PORT_CANDIDATES_DATA_KEY, TARGET_PORT_CANDIDATES_DATA_KEY, NODE_PORT_CANDIDATES_DATA_KEY, portPlacementPolicies
Gets a value indicating whether this instance is currently running.
readonlyfinal

Property Value

true if this instance has been started but has not yet finished; false otherwise.
Gets or sets a value indicating whether this instance waits for other instances to finish their operations before it executes.

The default value is true. In this case, this instance waits for other instances of LayoutExecutor that handle the same instance of GraphComponent to finish their operation before it executes.

If set to false, this instance ignores other potentially running instances, and doesn't try to stop them but rather executes immediately. Also it will not be stopped by other instances. This should only be used under special circumstances since it can result in race conditions if multiple animations or calculations are performed on the same graph instance.

final
Gets or sets the maximum runtime for the layout calculation before it is automatically stopped.
TExceeding this duration will terminate the algorithm gracefully. This setting only affects the layout calculation time and does not include time that is needed for layout animation.
conversion

Property Value

the duration to wait before the layout calculation is requested to stop. Automatic termination will only occur for positive values.

Throws

Exception ({ name: 'ArgumentError' })
if the duration is negative

See Also

API
stopDuration
Gets the tableLayoutConfigurator that is used if configureTableLayout is enabled.
Gets or sets the padding (in world coordinates) that will be added to the content bounds when calculating the target viewport.
The default value is EMPTY.
conversionfinal

Property Value

The target bounds padding.

Methods

Factory method that creates the IAnimation that will be used by this instance after the layout has been calculated.
protected

Return Value

IAnimation
The animation to use after the layout.

See Also

API
animationDuration, animateViewport, createLayoutAnimation, createViewportAnimation
Factory method that creates the animation for the IGraph.
protected

Return Value

IAnimation
The animation instance.

See Also

API
createAnimation
Creates the LayoutGraphAdapter which is used when a layout is executed.
protected

Return Value

LayoutGraphAdapter
A preconfigured LayoutGraphAdapter.

See Also

API
LayoutGraphAdapter
Creates an animation that transitions the layout of all ITables in the graph.
Create a new instance of TableLayoutConfigurator that is used if configureTableLayout is enabled.
This method is called upon first access to the tableLayoutConfigurator property.
protected

Return Value

TableLayoutConfigurator
A new instance of the TableLayoutConfigurator class.
Factory method that creates the animation for the viewport.
The created animation will change the current viewport into the one where the whole graph fits. The result after the animation is thus the same as calling fitGraphBounds.
protected

Parameters

targetBounds: Rect
The target bounds of the animation.

Return Value

IAnimation
The animation instance.

See Also

API
createAnimation
Callback method that performs the actual layout.
Calculate the target bounds to be used for the contentBounds as well as the ViewportAnimation after the layout has finished.
This implementation configures horizontalLayout according to the LayoutOrientation of the given layout and calls prepare.
protected

Return Value

LayoutGridData<INode, IEdge, ILabel, ILabel>
The configured LayoutGridData<TNode, TEdge, TNodeLabel, TEdgeLabel> instance that's applied to the graph.
Writes the table layout information provided through tableLayoutConfigurator back to all tables.
This method is only called when the layout is not animated.
protected

See Also

API
prepareTableLayout
Actually starts the layout calculation and the optional animation asynchronously using a Promise<void>.

This method will ultimately call the execute method. If the animationDuration is zero, no animation will be performed.

If this instance is already running, this method returns immediately without doing anything and returns the previous Promise<void>.

The layout algorithm itself is executed using a LayoutGraphAdapter instance which is created and configured in method createLayoutGraphAdapter.

Although this method returns a Promise and the animation will be performed asynchronously without blocking the UI thread, the actual calculation of the layout will block the UI thread. To mitigate this, check the advice in the developer's guide in the section Using Asynchronous Layout Execution .

Return Value

Promise<void>
A Promise<void> that will be fulfilled once the layout and optional animation is done.

See Also

API
execute, createLayoutGraphAdapter
Stops a currently running layout calculation or animation.

If a layout calculation is still running, it will be requested to stop via stop and the animation will not run. If the layout calculation was already completed, the animation will be aborted immediately and the layout result will be shown immediately.

To just skip the animation but let the calculation finish normally, the animationDuration can be set to zero at any time before the animation was started.

final

Return Value

Promise<void>
A promise that will resolve once the layout calculation or animation is stopped.

Static Methods

Makes sure the LayoutExecutor code is not stripped by build optimizations like tree shaking.
If a layout is run with the applyLayoutAnimated or applyLayout methods, this can happen, since these methods internally use LayoutExecutor but there is no explicit import of that class.
static

See Also

Developer's Guide