documentationfor yFiles for HTML 3.0.0.1

Ports

General

In yFiles for HTML 3.0, the former concepts of PortConstraints and PortCandidates have been combined into a single, more powerful concept. A possible port (that is, the side or even the exact location where an edge connects to its source or target) is now represented by the class LayoutPortCandidate. Additionally, the valid sides of ports are now specified using the property side. The previous basic side values NORTH, SOUTH, WEST, and EAST, provided by the enum PortSide have been renamed to TOP, BOTTOM, LEFT, and RIGHT, and are now defined in the enum PortSides.

The LayoutPortCandidate instances are typically not created directly but are instead part of NodePortCandidates (which are related to the former concept of PortCandidateSets) and EdgePortCandidates (which are related to the former concept of lists of PortCandidates). Most LayoutData classes for the major layout algorithm now have a ports property (for example, HierarchicalLayoutData.ports) that offer access to a new sub-data of type BasicPortData or PortData. These classes enable easy specification of port-related data for edges and nodes. Using class EdgePortCandidates demonstrates how to achieve this for different edges, and Using class NodePortCandidates shows how to specify the valid ports provided by a node.

Using class EdgePortCandidates
// create the layout algorithm and the corresponding layout data instance
const layout = new HierarchicalLayout()
const data = layout.createLayoutData(graph)

// the 'ports' sub-data provides all port-related data;
// we want to specify the ports of edges on both endpoints (source and target)
const spcMapper = data.ports.sourcePortCandidates.mapper
const tpcMapper = data.ports.targetPortCandidates.mapper

// the source port of edge e1 should be at the center on the right side
// and the target port on the top or bottom side
spcMapper.set(
  e1,
  new EdgePortCandidates().addFixedCandidate('right', new Point(15, 0))
)
tpcMapper.set(
  e1,
  new EdgePortCandidates()
    .addFreeCandidate('top')
    .addFreeCandidate('bottom')
)

// the source port of edge e2 should be on the left side
// and on the target we want to keep the current port which is on the bottom side
spcMapper.set(e2, new EdgePortCandidates().addFreeCandidate('left'))
tpcMapper.set(e2, new EdgePortCandidates().addFixedCandidate('bottom'))

Using class NodePortCandidates
// specify some ports at the top side of node n1 ...
data.ports.nodePortCandidates.mapper.set(
  n1,
  new NodePortCandidates()
    .addFixedCandidate('top', new Point(-5, -15))
    .addFixedCandidate('top', new Point(5, -15), 3.0, 2)
)

// ... and a left and right port for node n2
data.ports.nodePortCandidates.mapper.set(
  n2,
  new NodePortCandidates()
    .addFreeCandidate('left', 2.0)
    .addFixedCandidate('right', new Point(15, 0))
)

Note that the former strong port constraints (enforcing that the layout algorithm keeps the current port location) can now be modeled with the method addFixedCandidate, that is, by creating a fixed candidate without specifying an offset.

The former class PortConstraintKeys has been removed. In most cases, the port data can simply be specified with the new port-related sub-data as mentioned above. If this is not possible, the data can still be specified and registered directly with the layout graph using the following data keys:

With yFiles for HTML 3.0, most layout algorithms provide at least generic support for ports. More precisely, for algorithms that do not directly support port candidates with an integrated approach, the ports are fixed as a post-processing step by automatically applying the class PortPlacementStage. Therefore, most of the algorithms’ LayoutData classes provide a port-related sub-data via the property ports (see BasicPortData and its usages).

Matching between node and edge port candidates

The layout algorithms automatically attempt to match appropriate node and edge port candidates. In yFiles for HTML 3.0, the matching process also considers the new matchingId parameter as shown in Creating matching port candidates. Two candidates can only be matched if their matching-ids are equal, or if at least one of the matching-ids is null (a `null`matching-id - which is the default - matches any other matching-id).

Creating matching port candidates
const layout = new HierarchicalLayout()
const data = layout.createLayoutData(graph)

// all nodes should provide two fixed ports at the top and bottom side;
// the available ports are subdivided into "red" and "blue" ports
data.ports.nodePortCandidates.constant = new NodePortCandidates()
  .addFixedCandidate({
    side: PortSides.TOP,
    offset: new Point(-5, -15),
    matchingId: 'red'
  })
  .addFixedCandidate({
    side: PortSides.TOP,
    offset: new Point(5, -15),
    matchingId: 'blue'
  })
  .addFixedCandidate({
    side: PortSides.BOTTOM,
    offset: new Point(-5, 15),
    matchingId: 'red'
  })
  .addFixedCandidate({
    side: PortSides.BOTTOM,
    offset: new Point(5, 15),
    matchingId: 'blue'
  })

const spcMapper = data.ports.sourcePortCandidates.mapper
const tpcMapper = data.ports.targetPortCandidates.mapper

// the source port of edge e1 can be any "red" port provided by the source node on its top side
spcMapper.set(
  e1,
  new EdgePortCandidates().addFreeCandidate({
    side: PortSides.TOP,
    matchingId: 'red'
  })
)

// the target port of edge e1 can be any port provided by the target node (matches each matchingId)
tpcMapper.set(
  e1,
  new EdgePortCandidates().addFreeCandidate({ side: PortSides.ANY })
)

// the source port of edge e2 can be any "blue" port provided by the source node
spcMapper.set(
  e2,
  new EdgePortCandidates().addFreeCandidate({
    side: PortSides.ANY,
    matchingId: 'blue'
  })
)

// the target port of edge e2 can be any port provided by the target node on its bottom side
tpcMapper.set(
  e2,
  new EdgePortCandidates().addFreeCandidate({ side: PortSides.BOTTOM })
)