Rectangular Layout with ports inside

Currently I am using location to manually form rectangular layout. But my approach is not scalable because number of nodes and their size can vary. Is there a way to position nodes in rectangular shape for ex: like circular layout. Also I want to keep ports inside. For example for nodes that are on left side, ports will be right side of those nodes. For nodes on bottom, ports will be on top of nodes. I took reference from Dynamic Ports and manually assigned ports to nodes. But is there a way to position ports based on which side a node is present.

Thanks

Yes, automatically positioning the nodes in a particular manner is the responsibility of a Layout.

Do you have a requirement that certain nodes be on a specific side?

Do you want to have the layout also change the size of the nodes to match the number of links that are connecting on a side?

One possibility is to use PackedLayout with a dummy vertex in the center that has the size of the empty area where you want the links to be routed.

Another possibility is to implement your own custom Layout that basically does what you are doing now.

Do you have a requirement that certain nodes be on a specific side?

No, node can be any side, doesn’t matter.

Do you want to have the layout also change the size of the nodes to match the number of links that are connecting on a side

Yes, if you see screenshot, there is EMS_CONNECTOR which is big because of number of ports it has. Because of EMS_CONNECTOR only few nodes can be placed on same side. So, layout should adapt number of nodes per side based on node width (if horizontal direction), node height (if vertical direction)

Do you have some example how I can achieve it using PackedLayout ? I went through Packed Layout. But didn’t found way to put empty area inside

Will the number of ports be independent of the number of connected links? In other words, will some ports exist and be shown and yet have no links connected with it? And will some ports have more than one link connected?

Is there any implicit order in the ports? Do you want to show them in any particular order?

Can you share with us the model that you are using?

Here’s the start of a BorderLayout that lays out nodes evenly along the sides of a specified rectangular BorderLayout.area.

It assumes that there are no distinct ports on any node – instead it just connects links evenly on the side of each node where it borders the rectangular area. Because it does not know about ports on nodes, it does not modify the size of any node.

[EDIT: code obsoleted by code provided below]

Thanks for solution. But in my case, nodes have ports and node size can vary based on num of ports.

Here is sandbox of what I am trying to achieve: romantic-agnesi-sc1ctm - CodeSandbox

node template

$(
      go.Node,
      "Table",
      {
        locationObjectName: "BODY",
        selectionObjectName: "BODY"
      },
      new go.Binding(
        "locationSpot",
        "sideIndex",
        (v) => [go.Spot.Bottom, go.Spot.Top, go.Spot.Left, go.Spot.Right][v]
      ),

      new go.Binding("location", "loc", go.Point.parse).makeTwoWay(
        go.Point.stringify
      ),

      // the body
      $(
        go.Panel,
        "Auto",
        {
          row: 1,
          column: 1,
          name: "BODY",
          stretch: go.GraphObject.Fill
        },
        $(
          go.Shape,
          "Rectangle",
          {
            stroke: null,
            strokeWidth: 0,
            minSize: new go.Size(60, 60)
          },
          new go.Binding("fill", "color")
        ),
        $(
          go.TextBlock,
          {
            margin: 10,
            textAlign: "center",
            font: "bold 14px Segoe UI,sans-serif",
            stroke: "#dddddd"
          },
          new go.Binding("text", "name").makeTwoWay()
        )
      ), // end Auto Panel body

      // the Panel holding the left port elements, which are themselves Panels,
      // created for each item in the itemArray, bound to data.leftArray
      makePortTemplate(go.Panel.Vertical, "leftArray", go.Spot.Left, 1, 0),

      // the Panel holding the top port elements, which are themselves Panels,
      // created for each item in the itemArray, bound to data.topArray
      makePortTemplate(go.Panel.Horizontal, "topArray", go.Spot.TopSide, 0, 1),

      // the Panel holding the right port elements, which are themselves Panels,
      // created for each item in the itemArray, bound to data.rightArray
      makePortTemplate(go.Panel.Vertical, "rightArray", go.Spot.Right, 1, 2),

      // the Panel holding the bottom port elements, which are themselves Panels,
      // created for each item in the itemArray, bound to data.bottomArray
      makePortTemplate(
        go.Panel.Horizontal,
        "bottomArray",
        go.Spot.BottomSide,
        2,
        1
      )
    );

makePortTemplate:

const makePortTemplate = (
      direction,
      dataKey,
      spotDirection,
      row,
      column
    ) => {
      return $(go.Panel, direction, new go.Binding("itemArray", dataKey), {
        row: row,
        column: column,
        itemTemplate: $(
          go.Panel,

          {
            _side: "left", // internal property to make it easier to tell which side it's on
            fromSpot: spotDirection,
            toSpot: spotDirection,
            fromLinkable: true,
            toLinkable: true,
            cursor: "pointer"
          },
          new go.Binding("portId", "portId"),
          $(
            go.Shape,
            "Rectangle",
            {
              stroke: null,
              strokeWidth: 0,
              desiredSize: portSize,
              margin: new go.Margin(1, 0)
            },
            new go.Binding("fill", "portColor")
          ),
          $(
            go.TextBlock,
            {
              stroke: "white",
              font: "4pt serif",
              textAlign: "center",
              margin: 1
            },
            new go.Binding("text", "portLabel")
          )
        )
      });
    };

So I’ll assume that you have only one list of ports per node, and that you specify the order of the ports within each node.

I didn’t quite understand your question.

Each node has only ports on one side.
I didn’t get about order of ports. If you’re asking about left-to-right or right-to-left order (of horizontal nodes), then it doesn’t matter.

model data:

{
      nodeDataArray: [
        {
          key: 0,
          text: "Alpha",
          color: "lightblue",
          sideIndex: 0,
          loc: "10 10",
          bottomArray: [
            { portId: "0" },
            { portId: "1" },
            { portId: "2" },
            { portId: "3" },
            { portId: "4" },
            { portId: "5" },
            { portId: "6" }
          ]
        },
        {
          key: 1,
          text: "Beta",
          color: "orange",
          sideIndex: 0,
          loc: "100 10",
          bottomArray: [
            { portId: "0" },
            { portId: "1" },
            { portId: "2" },
            { portId: "3" },
            { portId: "4" }
          ]
        },
        {
          key: 2,
          text: "Gamma",
          color: "lightgreen",
          sideIndex: 1,
          loc: "-100 10",
          rightArray: [{ portId: "0" }, { portId: "1" }, { portId: "2" }]
        },
        {
          key: 3,
          text: "Delta",
          color: "pink",
          sideIndex: 1,
          loc: "-100 100",
          rightArray: [{ portId: "0" }, { portId: "1" }]
        },
        {
          key: 4,
          text: "Delta",
          color: "pink",
          sideIndex: 2,
          loc: "-10 240",
          topArray: [{ portId: "0" }, { portId: "1" }]
        },
        {
          key: 5,
          text: "Delta",
          color: "pink",
          sideIndex: 2,
          loc: "80 240",
          topArray: [
            { portId: "0" },
            { portId: "1" },
            { portId: "2" },
            { portId: "3" },
            { portId: "4" },
            { portId: "5" },
            { portId: "6" }
          ]
        },
        {
          key: 6,
          text: "Delta",
          color: "pink",
          sideIndex: 3,
          loc: "260 80",
          leftArray: [
            { portId: "0" },
            { portId: "1" },
            { portId: "2" },
            { portId: "3" },
            { portId: "4" },
            { portId: "5" },
            { portId: "6" }
          ]
        }
      ],
      linkDataArray: [
        { key: -1, from: 0, to: 1, fromPort: "2", toPort: "1" },
        { key: -2, from: 0, to: 2, fromPort: "6", toPort: "1" },
        { key: -3, from: 1, to: 4, fromPort: "2", toPort: "1" },
        { key: -4, from: 2, to: 3, fromPort: "2", toPort: "1" },
        { key: -5, from: 3, to: 0, fromPort: "1", toPort: "1" },
        { key: -6, from: 6, to: 2, fromPort: "1", toPort: "1" },
        { key: -7, from: 3, to: 4, fromPort: "2", toPort: "0" },
        { key: -8, from: 5, to: 3, fromPort: "2", toPort: "0" },
        { key: -9, from: 5, to: 4, fromPort: "2", toPort: "0" },
        { key: -10, from: 2, to: 3, fromPort: "2", toPort: "0" },
        { key: -11, from: 1, to: 3, fromPort: "2", toPort: "1" }
      ],
      modelData: {
        canRelink: true
      },
      selectedData: null,
      skipsDiagramUpdate: false
    }
  1. Yes, there might be ports without any links.
  2. Yes, it’s possible.

[EDIT: the sample has been further improved and published as Border Layout (External)]

Note that I removed the sideIndex and loc data properties from the node data.

Note also that it appears that at least one of the links in your model data do not correctly specify port identifiers, causing the link to connect with the whole node instead of with an individual port element.

Thanks for the quick solution, this is what I am looking for.

An even more improved version is now at: Border Layout (External)

How to access this Border Layout (External) code?
#walter

As always, the complete implementation is in the page itself. Either use curl, execute View Page Source command, or look at view-source:https://gojs.net/extras/BorderLayoutExternal.html