SwimLaneLayout node order issue

Hi,

I am using the SwimLaneLayout and adopt its sample code.

I noticed that the node order affects the link routing. For example, the following nodeDataArray causes the links between the 2nd level and the 3rd level intertwined

[
  {
    key: "A",
    isGroup: true,
  },
  {
    key: "Box1",
    group: "A",
  },
  {
    key: "Box2",
    group: "A",
  },
  {
    key: "Box3",
    group: "A",
  },
  {
    key: "Box4",
    group: "A",
  },
  {
    key: "Box5",
    group: "A",
  },
  {
    key: "Box6",
    group: "A",
  },
];

But if I move Box6 before Box3, the layout becomes much clear.

[
  {
    key: "A",
    isGroup: true,
  },
  {
    key: "Box1",
    group: "A",
  },
  {
    key: "Box2",
    group: "A",
  },
  {
    key: "Box6",
    group: "A",
  },
  {
    key: "Box3",
    group: "A",
  },
  {
    key: "Box4",
    group: "A",
  },
  {
    key: "Box5",
    group: "A",
  },
];

However, such node ordering issue does not exist with standard LayeredDigraphLayout. Is it a bug in SwimLaneLayout or is there some configuration that I can apply so that the link routing is not affected by the node order.

My complete HTML page including the JS code is below. Thanks so much for your time!

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <div
      id="myDiagramDiv"
      style="border: solid 1px black; width: 100%; height: 700px"
    ></div>
    <script src="../../release/go-debug.js"></script>
    <script src="../../extensions/SwimLaneLayout.js"></script>
    <script>
      function init() {
        const $ = go.GraphObject.make;
        myDiagram = $(go.Diagram, "myDiagramDiv", {
          layout: $(SwimLaneLayout, {
            laneProperty: "group", // needs to know how to assign vertexes/nodes into lanes/groups
            setsPortSpots: false,
            layerSpacing: 20,
            columnSpacing: 5,
            commitLayers: function (layerRects, offset) {
              if (layerRects.length === 0) return;

              var horiz = true;
              var forwards = true;

              var rect = layerRects[forwards ? layerRects.length - 1 : 0];
              var totallength = horiz ? rect.right : rect.bottom;

              for (var i = 0; i < this.laneNames.length; i++) {
                var lane = this.laneNames[i];
                // assume lane names do not conflict with node names
                var group = this.diagram.findNodeForKey(lane);
                if (group === null) {
                  this.diagram.model.addNodeData({ key: lane, isGroup: true });
                  group = this.diagram.findNodeForKey(lane);
                }
                if (horiz) {
                  group.location = new go.Point(
                    -this.layerSpacing / 2,
                    this.lanePositions.get(lane) * this.columnSpacing +
                      offset.y,
                  );
                } else {
                  group.location = new go.Point(
                    this.lanePositions.get(lane) * this.columnSpacing +
                      offset.x,
                    -this.layerSpacing / 2,
                  );
                }
                var ph = group.findObject("PLACEHOLDER"); // won't be a go.Placeholder, but just a regular Shape
                if (ph === null) ph = group;
                if (horiz) {
                  ph.desiredSize = new go.Size(
                    totallength,
                    this.laneBreadths.get(lane) * this.columnSpacing,
                  );
                } else {
                  ph.desiredSize = new go.Size(
                    this.laneBreadths.get(lane) * this.columnSpacing,
                    totallength,
                  );
                }
              }
            },
          }),
        });

        myDiagram.nodeTemplate = $(
          go.Node,
          "Spot",
          new go.Binding("location", "loc", go.Point.parse),
          $(go.Shape, "RoundedRectangle", {
            fill: "white",
            width: 100,
            height: 50,
            portId: "",
            fromLinkable: true,
            toLinkable: true,
            cursor: "pointer",
          }),
          $(
            go.TextBlock, // the text label
            new go.Binding("text", "key"),
            {
              verticalAlignment: go.Spot.Center,
              textAlign: "center",
            },
          ),
        );

        myDiagram.linkTemplate = $(
          go.Link,
          { routing: go.Link.AvoidsNodes, corner: 10 },
          $(go.Shape, { strokeWidth: 1.5 }),
          $(go.Shape, { toArrow: "Standard", stroke: null }),
        );

        myDiagram.groupTemplate = $(
          go.Group,
          "Horizontal",
          {
            layerName: "Background",
            movable: false,
            copyable: false,
            locationObjectName: "PLACEHOLDER",
            layout: null,
            avoidable: false,
          },
          $(
            go.TextBlock,
            {
              font: "bold 12pt sans-serif",
              angle: 270,
            },
            new go.Binding("text", "key"),
          ),
          $(
            go.Panel,
            "Auto",
            $(go.Shape, { fill: "transparent", stroke: "orange" }),
            $(go.Shape, {
              name: "PLACEHOLDER",
              fill: null,
              stroke: null,
              strokeWidth: 0,
            }),
          ),
        );

        const nodeDataArray = [
          {
            key: "A",
            isGroup: true,
          },
          {
            key: "Box1",
            group: "A",
          },
          {
            key: "Box2",
            group: "A",
          },
          {
            key: "Box6",
            group: "A",
          },
          {
            key: "Box3",
            group: "A",
          },
          {
            key: "Box4",
            group: "A",
          },
          {
            key: "Box5",
            group: "A",
          },
        ];

        const linkDataArray = [
          {
            from: "Box1",
            to: "Box2",
          },
          {
            from: "Box1",
            to: "Box3",
          },
          {
            from: "Box2",
            to: "Box6",
          },
          {
            from: "Box3",
            to: "Box4",
          },
          {
            from: "Box3",
            to: "Box5",
          },
        ];

        const model = new go.GraphLinksModel();
        model.nodeGroupKey = "group";
        model.nodeDataArray = nodeDataArray;
        model.linkDataArray = linkDataArray;
        myDiagram.layout.laneProperty = model.nodeGroupKey;
        myDiagram.layout.laneNames = ["A"];
        myDiagram.model = model;
      }

      window.addEventListener("DOMContentLoaded", init);
    </script>
  </body>
</html>

Here’s some experimental code that you can try: https://gojs.net/temp/CrossingsReducer.js

Load that code (sorry, it’s still old style JavaScript) and then set initialize SwimLaneLayout.reducer:

    reducer: new CrossingsReducer(),

I hope that helps. I have not tried it with your data.

Thank you, Walter. I will try it and get back to you.