documentationfor yFiles for HTML 3.0.0.1

Scope and Affected Items

The scope of a layout algorithm defines which graph elements are affected by the algorithm. yFiles for HTML 3.0 offers a more uniform scope API, designed to simplify the configuration of common use cases.

Restricting the Scope

The properties for restricting the scope of layout algorithms have been unified and, where possible, moved to the layout data. These layout data classes now offer a scope property, e.g., EdgeRouterData.scope, that supports any convenience that was previously offered on the layout algorithms, such as restricting affected edges by specifying incident nodes.

const edgeRouter = new EdgeRouter()
const edgeRouterData = edgeRouter.createLayoutData()
edgeRouterData.scope.incidentNodes.source = graphControl.selection.nodes

Convenience features that depend on an intermediate state of the layout, such as the EdgeRouter only handling broken paths, can now be assigned for individual elements through the Scope property.

edgeRouterData.scope.edgeMapping.mapperFunction = (edge) =>
  edge.tag === 'Route if necessary'
    ? EdgeRouterScope.PATH_AS_NEEDED
    : EdgeRouterScope.IGNORE

edgeRouterData.scope.edgeMapping.mapperFunction = (edge: IEdge) =>
  edge.tag === 'Route if necessary'
    ? EdgeRouterScope.PATH_AS_NEEDED
    : EdgeRouterScope.IGNORE

The GenericLabeling algorithm is a notable exception to this general rule. While it supports the same ways of specifying the affected labels, it also retains functionality to restrict the algorithm to EdgeLabels or NodeLabels only on the labeling algorithm itself, since this use case was common enough to warrant a deviation from the norm. This convenience can theoretically be combined with a custom selection specified via the labeling data’s scope property to further restrict the affected labels. However, in such cases, it would be simpler and more maintainable to fully specify the scope on the layout data.

const genericLabeling = new GenericLabeling()
genericLabeling.scope = 'edge-labels'

Shared Scopes

In a layout pipeline that consists of multiple layout stages, it is often necessary for all algorithms to operate on the same subset of graph elements. Consequently, yFiles for HTML 3.0 introduces a single scope that only has to be set once for all layout algorithms. This replaces the individual scope-related data keys on the layout algorithms and affects all simple boolean states configurable through the layout data’s scope properties, as well as values registered directly with the layout graph using the LayoutKeys:

As a result, writeable DataKeys such as RecursiveGroupLayout.interEdgesDpKey and EdgeRouter.affectedEdgesDataKey, that previously had to be manually synchronized to enable communication between the algorithms, are no longer necessary and were removed in yFiles for HTML 3.0. The communication between algorithms is now established automatically, using the new shared scope information.

However, scope information that relies on conditions more complex than a boolean value, such as OrganicScope, is still associated specifically with the corresponding layout algorithm.

For use cases where layout stages of the same layout pipeline must operate on different scopes, the ContextModificationStage can be used to temporarily change the data associated with any of the layout keys mentioned above, or even to temporarily apply an alternative layout data.

const edgeRouter = new EdgeRouter() // apply the straight line router immediately before the edge router
const edgeRouterData = edgeRouter.createLayoutData()
edgeRouterData.scope.edges.predicate = (e) => e.tag === 'routed' // route marked edges

const straightLineRouter = new StraightLineEdgeRouter()
const contextModificationStage = new ContextModificationStage(
  straightLineRouter
) // temporarily change context for StraightLineRouter
contextModificationStage.addReplacementMap(
  LayoutKeys.ROUTE_EDGES_DATA_KEY,
  IMapper.fromHandler((e) => e.tag === 'straight-line')
)

edgeRouter.coreLayout = contextModificationStage

const edgeRouter = new EdgeRouter() // apply the straight line router immediately before the edge router
const edgeRouterData = edgeRouter.createLayoutData()
edgeRouterData.scope.edges.predicate = (e: IEdge) => e.tag === 'routed' // route marked edges

const straightLineRouter = new StraightLineEdgeRouter()
const contextModificationStage = new ContextModificationStage(
  straightLineRouter
) // temporarily change context for StraightLineRouter
contextModificationStage.addReplacementMap(
  LayoutKeys.ROUTE_EDGES_DATA_KEY,
  IMapper.fromHandler<LayoutEdge, boolean>(
    (e: LayoutEdge) => e.tag === 'straight-line'
  )
)

edgeRouter.coreLayout = contextModificationStage