kieler / elkjs

ELK's layout algorithms for JavaScript
Other
1.81k stars 97 forks source link

[Question] Layered algorithm - Specify horizontal nodes ordering #299

Closed sagunji closed 2 months ago

sagunji commented 2 months ago

I'm working on a workflow graph visualization using ELK.js, and I'm facing challenges with node placement in graphs that have multiple and nested conditional branches.

Objective:

How can I configure ELK.js to consistently position nodes from True branches on the left and nodes from False branches on the right in a workflow graph with multiple and nested conditional branches?

Additional Context:

Snippet:

const graph = {
  id: 'root',
  layoutOptions: {
    'elk.algorithm': 'layered',
    'elk.direction': 'DOWN',
    'elk.edgeRouting': 'POLYLINE',
    'elk.layered.crossingMinimization.semiInteractive': true,
    // Other layout options...
  },
  children: nodes.map(node => ({
    id: node.id,
    width: node.width ?? 0,
    height: node.height ?? 0,
    // Attempting to set 'elk.index' based on branchSide
    'elk.index': node.data?.branchSide === 'True' ? 0 : 1,
  })),
  edges: edges.map(edge => ({
    id: edge.id,
    sources: [edge.source],
    targets: [edge.target],
    // Edge properties...
  })),
};

Current output:

image
sagunji commented 2 months ago

Ok, I think with portConstraints I can anchor the side. Elk Live link: https://rtsys.informatik.uni-kiel.de/elklive/elkgraph.html?compressedContent=IYGw5g9gTglgLgCwLYC4AEJgE8CmUcAmAUDiANYB0BM+AxnDBAHboAiA8gOoByJ5FhMDgBKEAK4MmYdAAV2AGQCa8gJLcAokSJMIBHGiYBGNAG8iaCxmAAjUmgBEAZzjAoce0QC+23fqYAmCzNLK1sQBz1MLA9vHT0DAGZTc0sAB2g4AGFmZyhgGCY4R3QAMRUADXVWAH12YVZ1YRSLdLc0VOMTNEcYPXQAZXYAVQAVAAk0Ar0AD3RjbxDWuHbArp6+tEHRiamcWbQABjQFy0wwhzgAegAzezQvH3imABZkkLO7e3UkfJAAWkMMUefgArG9TjZPt9fn9-EC4n4AOzgiwfcL2ABWEAK8N8BgAHCjQp9WKRsLingBOIlohzqJgEIHAgyGI7BNIZbJMXL5QrFNBlSo1OoNJqLDLtTrdXo4AbDcaTBl7ObHZrtCWpVbSjZbBW7fZHE6oyHouB-W4PZlGTpq2n2WiAh4IlmrW0mhy0OGW51GJLs43nTHYphMwR+Yx-AB8Bn8JAIQhjaCjiTjCaYCQoHST0ZeNPd9hGUDEOCBYcSmcCyaYYP9xPRJVAjhLlrLuariNTSOzBM7BO7TEpver-dZQ9ZmYjOcMNve+ZGpfj4YOFZH-lrdpKC7T05HCTHlane9bhiS7aIQA

Bug-Capture-DQGOBrslKrhduob5sP2q-MfLMTBrfrMjh5zD-IdYoGNq.webm

soerendomroes commented 2 months ago

Yes, portConstraints work.

sagunji commented 2 months ago

For anyone in the future.

const elkLayout: LayoutAlgorithm = async (nodes, edges, options) => {
  const graph = {
    id: 'elk-root',
    layoutOptions: {
      'elk.algorithm': 'layered',
      'elk.direction': 'DOWN',
      'elk.edgeRouting': 'POLYLINE',
      'elk.spacing.nodeNode': options.spacing[0],
      'elk.spacing.edgeNode': options.spacing[1],
      'elk.layered.spacing.nodeNodeBetweenLayers': '120',
    },
    children: nodes.map(node => {
      const isBranchTask = node.type === 'Branch';

      return {
        id: node.id,
        width: node.width ?? 0,
        height: node.height ?? 0,
        ...(isBranchTask
          ? {
            properties: {
              'org.eclipse.elk.portConstraints': 'FIXED_ORDER',
            },
            ports: [
              { id: node.id },
              {
                id: `${node.id}-match`,
                properties: {
                  side: 'SOUTH',
                  index: 1,
                },
              },
              {
                id: `${node.id}-nomatch`,
                properties: {
                  side: 'SOUTH',
                  index: 0,
                },
              },
            ],
          }
          : {}),
      };
    }),
    edges: edges.map(edge => {
      return {
        id: edge.id,
        sources: [edge.sourceHandle || edge.source],
        targets: [edge.target],
      };
    }),
  };
soerendomroes commented 2 months ago

Specifically, the following does the trick: 'org.eclipse.elk.portConstraints': 'FIXED_ORDER', on a node "fixes" the order of the ports and also requires them to be constrained to a specific side. Here, side: 'SOUTH' sets the desired port side, while index: 1 determines the ordering of both properties are set on a port of a node that has port constraints specified.