C

LayoutExecutorAsync

Executes an algorithm or ILayoutAlgorithm on a graph asynchronously and optionally animates the transition to the new layout, afterward.
Inheritance Hierarchy

Remarks

In contrast to LayoutExecutor, this class serves as one half of the pair of two classes that perform the calculation in separate contexts, asynchronously to the main thread that starts the layout. This allows the main thread to continue and the UI to stay responsive when longer layout calculations need to be performed, or multiple layout calculations should be carried out in parallel.

Instances of this class will serialize all the information that is required to execute the algorithm and send that information to a separate context, which processes the data with the help of the LayoutExecutorAsyncWorker class. The context that worker executes in often is a Web Worker, but it could also be running on a different machine or a server.

The actual transmission of the serialized data to the other context is not part of this implementation. Rather, it can be added by means of passing a function callback to the constructor. That function will be given an opaque blob that can be serialized and sent to the second context where it will be processed by a call to process. Once the the response is received from the other context, the function callback resolves the Promise and the results are applied to the graph.

See Also

The ability to execute layouts asynchronously via web workers is presented in detail in the section Asynchronous Layout Calculation .

Developer's Guide

API

LayoutExecutorAsyncWorker, LayoutExecutor

Members

Show:

Constructors

Creates a new instance of a layout execution helper that will asynchronously perform the calculations and apply the results on the provided graph.
Without a GraphComponent, no animation can be performed. If you have a GraphComponent, use the constructor variant that takes the component, instead, in order to enable animation. In order to execute the layout, the messageHandler will be given the serialized information to pass to the layout worker. Once the worker returns the results, the handler will need to resolve the Promise and the results will be deserialized and applied to the graph.

Parameters

messageHandler: function(object): Promise<object>

The function that will send the data to the remote worker. It's an asynchronous function that will need to resolve it's Promise once it receives the processed data from the worker. The parameter passed to (and returned from) the function is JSON serializable and structurally cloneable . Otherwise, the contents of the data blob are considered an implementation detail and may change at any time between releases.

When no additional data has to be passed between the threads, the convenience method createWebWorkerMessageHandler can be used to create the message handler.

graph: IGraph
The graph that will be serialized and transferred to the worker.
layoutDescriptor?: LayoutDescriptor
The optional descriptor to serialize and send to the worker. This property can later be set and reset via the layoutDescriptor property.
layoutData?: LayoutData<INode, IEdge, ILabel, ILabel>
The optional data that is associated with the graph items that needs to be transferred to the worker. This property can also later be set and reset via the layoutData property.
Creates a new instance of a layout execution helper that will asynchronously perform the calculations and optionally animate the layout on the given graphComponent.
In order to execute the layout, the messageHandler will be given the serialized information to pass to the layout worker. Once the worker returns the results, the handler will need to resolve the Promise and the results will be deserialized and applied to the graph. If you don't have a GraphComponent, use the constructor variant that takes the IGraph, instead.

Parameters

messageHandler: function(object): Promise<object>

The function that will send the data to the remote worker. It's an asynchronous function that will need to resolve it's Promise once it receives the processed data from the worker. The parameter passed to (and returned from) the function is JSON serializable and structurally cloneable . Otherwise, the contents of the data blob are considered an implementation detail and may change at any time between releases.

When no additional data has to be passed between the threads, the convenience method createWebWorkerMessageHandler can be used to create the message handler.

graphComponent: GraphComponent
The graph component from which the graph and selection will be taken and in which the animation will be performed.
layoutDescriptor?: LayoutDescriptor
The optional descriptor to serialize and send to the worker. This property can later be set and reset via the layoutDescriptor property.
layoutData?: LayoutData<INode, IEdge, ILabel, ILabel>
The optional data that is associated with the graph items that needs to be transferred to the worker. This property can also later be set and reset via the layoutData property.

Properties

Gets or sets a value indicating whether user interaction should be allowed during the execution and animation.

If false, the WaitInputMode is queried from the CanvasComponent and waiting is enabled during the animation.

The default value is false.

final
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 property only takes effect if the core layout algorithm is wrapped with an instance of LayoutAnchoringStage on the worker side.
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.
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.
Gets or sets the duration a layout may run before being cancelled automatically.
The algorithm is terminated immediately, then. This setting only affects the layout runtime in the other worker context and does not include time that is needed for communication and de-/serialization of the data between the different contexts.
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 automatically perform calls to prepare and restore in order to layout table nodes.
The default value is true.
final

Property Value

true if table layout should be configured automatically; false otherwise.
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 value indicating whether to use eased animation.
The default value is true.
final

Property Value

true if the animation should be done with eased; false otherwise.
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 a mapping from edges in the LayoutGraph to their new layout, after the results are in.
It is safe to use this property during getTargetBounds and other methods that will be called after the results are available. If the results are not yet available, accessing this property will result in an exception.
protectedreadonlyfinal

Throws

Exception ({ name: 'InvalidOperationError' })
Throws when the results are not yet available.

See Also

API
isResultAvailable
Gets or sets the size of the nodes that are inserted for the ports that are created for IEdges that are connected at other IEdges.
If this instance is configured to create dummy nodes for ports at edges, this will be the initial size of the dummy nodes with the center of the node always being the location of the port. The default is 3x3.
conversionfinal

See Also

API
hideEdgesAtEdges
Gets the graph this instance is working on.
This value can only be set via the constructor
protectedreadonlyfinal

Property Value

The graph.
Gets the component this instance has been created for.
This value can only be set via the constructor
protectedreadonlyfinal

Property Value

The component or null if this instance runs without a graph component.
Gets or sets a value that controls whether edges at other edges will be hidden from the layout graph or included.
If this property is set to true, edges at other edges are simply ignored and not part of the layout graph. The default is false in which case helper nodes are inserted into the layout graph for each source and target port of an IEdge that is owned by an IEdge.
final

See Also

API
hideEdgesAtEdges
Gets a value indicating whether the results of the current run have already been calculated and returned successfully to this instance.
readonlyfinal
Gets a mapping from labels in the LayoutGraph to their new layout, after the results are in.
It is safe to use this property during getTargetBounds and other methods that will be called after the results are available. If the results are not yet available, accessing this property will result in an exception.
protectedreadonlyfinal

Throws

Exception ({ name: 'InvalidOperationError' })
Throws when the results are not yet available.

See Also

API
isResultAvailable
Gets a mapping from labels in the LayoutGraph to their new layout parameters, after the results are in.
It is safe to use this property during getTargetBounds and other methods that will be called after the results are available. If the results are not yet available, accessing this property will result in an exception.
protectedreadonlyfinal

Throws

Exception ({ name: 'InvalidOperationError' })
Throws when the results are not yet available.

See Also

API
isResultAvailable
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 layoutDescriptor: LayoutDescriptor = {
  name: 'HierarchicalLayout',
}
await new LayoutExecutorAsync({
  messageHandler: webWorkerMessageHandler,
  graphComponent: graphComponent,
  layoutDescriptor: layoutDescriptor,
  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 layoutDescriptor: LayoutDescriptor = {
  name: 'HierarchicalLayout',
}
await new LayoutExecutorAsync({
  messageHandler: webWorkerMessageHandler,
  graphComponent: graphComponent,
  layoutDescriptor: layoutDescriptor,
  labelPlacementPolicies: (label) =>
    label.owner instanceof INode
      ? LabelPlacementPolicy.PREFER_MODEL
      : LabelPlacementPolicy.PREFER_PARAMETER,
}).start()

See Also

API
EDGE_LABEL_PREFERRED_PLACEMENT_DATA_KEY, labelPlacementPolicies
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

See Also

Developer's Guide
API
layoutDescriptor, LayoutExecutorAsyncWorker
Gets or sets the descriptor that will be sent to the worker.
The descriptor can be used to carry information from here to worker in order to configure the layout dynamically. For the static use-case this property can be ignored. The worker will then need to create and configure the layout without that information. Values stored here need to be JSON serializable in order to be passed to the worker.
final

See Also

Developer's Guide
API
LayoutData, LayoutExecutorAsyncWorker
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 a mapping from nodes in the LayoutGraph to their new layout, after the results are in.
It is safe to use this property during getTargetBounds and other methods that will be called after the results are available. If the results are not yet available, accessing this property will result in an exception.
protectedreadonlyfinal

Throws

Exception ({ name: 'InvalidOperationError' })
Throws when the results are not yet available.

See Also

API
isResultAvailable
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 layoutDescriptor: LayoutDescriptor = {
  name: 'HierarchicalLayout',
}
await new LayoutExecutorAsync({
  messageHandler: webWorkerMessageHandler,
  graphComponent: graphComponent,
  layoutDescriptor: layoutDescriptor,
  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 layoutDescriptor: LayoutDescriptor = {
  name: 'HierarchicalLayout',
}
await new LayoutExecutorAsync({
  messageHandler: webWorkerMessageHandler,
  graphComponent: graphComponent,
  layoutDescriptor: layoutDescriptor,
  portAdjustmentPolicies: (port) =>
    port.owner instanceof INode
      ? PortAdjustmentPolicy.SHORTEN
      : PortAdjustmentPolicy.LENGTHEN,
}).start()

See Also

API
portAdjustmentPolicies
Gets a mapping from ports in the LayoutGraph to their new location parameters, after the results are in.
It is safe to use this property during getTargetBounds and other methods that will be called after the results are available. If the results are not yet available, accessing this property will result in an exception.
protectedreadonlyfinal

Throws

Exception ({ name: 'InvalidOperationError' })
Throws when the results are not yet available.

See Also

API
isResultAvailable
Gets or sets how ILabels at IPorts should be treated by the layout algorithm.
This mapping is queried for each ILabels 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 layoutDescriptor: LayoutDescriptor = {
  name: 'HierarchicalLayout',
}
await new LayoutExecutorAsync({
  messageHandler: webWorkerMessageHandler,
  graphComponent: graphComponent,
  layoutDescriptor: layoutDescriptor,
  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 layoutDescriptor: LayoutDescriptor = {
  name: 'HierarchicalLayout',
}
await new LayoutExecutorAsync({
  messageHandler: webWorkerMessageHandler,
  graphComponent: graphComponent,
  layoutDescriptor: layoutDescriptor,
  portLabelPolicies: (label) =>
    label.owner instanceof IPort && label.owner.owner instanceof INode
      ? PortLabelPolicy.NODE_LABEL
      : PortLabelPolicy.EDGE_LABEL,
}).start()

See Also

API
PortLabelPolicy
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 layoutDescriptor: LayoutDescriptor = {
  name: 'HierarchicalLayout',
}
await new LayoutExecutorAsync({
  messageHandler: webWorkerMessageHandler,
  graphComponent: graphComponent,
  layoutDescriptor: layoutDescriptor,
  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 layoutDescriptor: LayoutDescriptor = {
  name: 'HierarchicalLayout',
}
await new LayoutExecutorAsync({
  messageHandler: webWorkerMessageHandler,
  graphComponent: graphComponent,
  layoutDescriptor: layoutDescriptor,
  portPlacementPolicies: (port) =>
    port.owner instanceof INode
      ? PortPlacementPolicy.KEEP_PARAMETER
      : PortPlacementPolicy.PREFER_MODEL,
}).start()

See Also

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.
Running implies that the remote worker is still working on the results or the animation is still in progress.
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, and LayoutExecutorAsync respectively, that work on the same instance of GraphComponent to finish their operation before it starts execution.

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 duration a layout may run before being stopped automatically.
The algorithm is terminated gracefully, then. This setting only affects the layout runtime in the other worker context and does not include time that is needed for communication and de-/serialization of the data between the different contexts.
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.
readonlyfinal

See Also

API
createTableLayoutConfigurator
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.
Gets or sets a value indicating whether the contentBounds property of the graphComponent should be updated upon completion.
The default value is true.
final

Property Value

true if the content bounds should be updated; false otherwise.

Methods

Cancels a currently running layout calculation or animation.

If a layout calculation is still running, the results of the other LayoutExecutorAsyncWorker are discarded and not applied to the graph. If the layout calculation was already completed and an animation is running, it will be aborted immediately and the layout result will be applied and shown immediately.

The promise returned from start is resolved immediately, without waiting for pending layout results. Results returned from running workers are silently discarded when passed on to this instance. The workers are not terminated when this method was called.

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 canceled.
Factory method that creates the IAnimation that will be used by this instance after the layout has been calculated.
This will only be called when GraphComponent is set and an animation is required.
protected

Return Value

IAnimation
The animation to use after the layout.

See Also

API
animationDuration, animateViewport, createLayoutAnimation, createViewportAnimation
Callback factory method that creates the IRectangle for the given IPort that is used as a dummy to represent the port at the IEdge that owns port.
protected

Parameters

port: IPort
The port to create the layout for.

Return Value

IRectangle
An IRectangle that uses the port's location as the center of the node.

See Also

API
edgePortNodeSize, hideEdgesAtEdges
Factory method that creates the animation for the IGraph.
This will only be called when GraphComponent is set and an animation is required.
protected

Return Value

IAnimation
The animation instance.

See Also

API
createAnimation
Creates an animation that morphs the layout of all ITables in the graph.
This will only be called when GraphComponent is set and an animation is required.
protected

Return Value

IAnimation
The animation that morphs the layout of all ITables in the graph.

See Also

API
IAnimation, configureTableLayout
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 morph the current viewport into the one where the whole graph fits. The result after the animation is thus the same as calling fitGraphBounds. This will only be called when GraphComponent is set and an animation is required.
protected

Parameters

targetBounds: Rect
The target bounds of the animation.

Return Value

IAnimation
The animation instance.

See Also

API
createAnimation
Calculate the target bounds to be used for the contentBounds as well as the ViewportAnimation after the layout has finished.
Sets up partition grid information from tables in the graph.
The default implementation also sets the horizontalLayout property depending on the layoutDescriptor's "layoutOrientation" property.
protected

Return Value

LayoutGridData<INode, IEdge, ILabel, ILabel>
A LayoutGridData<TNode, TEdge, TNodeLabel, TEdgeLabel> instance for all tables in the graph, or null if no LayoutGrid is necessary.
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
Triggers the asynchronous execution and returns a Promise that resolves when the calculation is done.

This will serialize the LayoutGraph, the layoutDescriptor, and the LayoutData<TNode, TEdge, TNodeLabel, TEdgeLabel> and send that information via the callback function that was passed to the constructor at construction time to the remote worker

After the results have been returned, they will be parsed by this instance, the results will be applied to the graph, either in an animated fashion or directly.

final

Return Value

Promise<void>
The promise to track the progress of the execution.

Throws

Exception ({ name: 'Error' })

Static Methods

Convenience method for creating a default message handler for a web worker that can be passed to the LayoutExecutorAsync constructor.
The created message handler returns a Promise that is resolved when it receives the result of a layout calculation from a LayoutExecutorAsyncWorker. Messages that are not sent by a LayoutExecutorAsyncWorker are ignored.
static

Parameters

worker: Worker
The web worker instance that the messages should be exchanged with.

Return Value

function(object): Promise<object>
A default message handler for the given web worker instance.

See Also

Developer's Guide
API
initializeWebWorker