Align link to port center

I am creating ports using fractional values in go.Spot. But the links going out and in to ports that are not in the middle have alignments issues.

I have attached a screenshot to explain it better.

Is there any alignment configuration that is needed?

This is my port creation function -

function makePort(name, spot) {
  return $(
    go.Panel,
    'Auto',
    {
      desiredSize: new go.Size(10, 10),
      alignment: spot,
      fromSpot: spot,
      toSpot: spot,
      cursor: 'pointer',
      fromLinkable: true,
      toLinkable: true,
    },
    new go.Binding('portId', 'key', key => `${name}::${key}`),
    $(go.Shape, 'Rectangle', {
      fill: lightBlue[50],
      stroke: null,
      desiredSize: new go.Size(10, 10),
    }),
    $(go.Shape, 'XLine', {
      fill: null,
      alignment: go.Spot.Center,
      stroke: blueGrey[500],
      desiredSize: new go.Size(5, 5),
    })
  );
}

Also, I use this to display the ports on mouseOver a node. But cannot figure out how to get access to the XLine shape in the port here -

/**
 * function to conditionally make ports visible
 * @param {go.Node} node
 * @param {boolean} show
 */
function showPorts(node, show) {
  node.ports.each(function(port) {
    if (port.portId) {
      // change port XLine stroke color here
    }
  });
}

As is well documented, you shouldn’t be setting the desiredSize/width/height of the border shape (i.e. the main element) of an “Auto” panel. The panel will size it for you – in this case that means the desiredSize that you set on the whole panel.

Are those links actually connecting with their respective ports? In other words, are Link.fromPort and Link.toPort actually port objects in their respecting Nodes?

For your other question about setting the stroke of the “XLine” shape, either give that a GraphObject.name so that you can call Panel.findObject, or if you have a reference to a port, its port.elt(1) will be the second element, which in that case will be the “XLine” Shape.

The port.elt(1) worked out great. Thanks.

Removed the desiredSize from the Panel. Will keep that in mind for future as well.

You are correct the Link.fromPort and Link.toPort are not getting picked up on link creation. Any reason why? I am following what was in this example.

First, check that you have set GraphLinksModel.linkFromPortIdProperty and linkToPortIdProperty. GoJS Ports in Nodes-- Northwoods Software

Second, check that your link data have those two properties set on them, in addition to the “from” and “to” properties (or whatever you have set for GraphLinksModel.linkFromKeyProperty and linkToKeyProperty).

Third, if the user is drawing those links interactively, make sure that the ports are fromLinkable or toLinkable or both. I see above that you have already done that. GoJS Validation -- Northwoods Software

That’s all set. I have other nodes that use ports different from and to ports on either side and they work fine. They have fromPort and toPort set up working correctly.

The alignment issue exists even after fromPort and toPort values are set.

This is the serialized form. It is because of the alignment property on the XLine shape?

I don’t think the “XLine” Shape alignment is a problem.

If you experiment by removing the Binding on Link.points, do you still have the problem?

Yea I thought of that as well. Did not make any difference.

In re-reading this topic, I noticed that I did not make myself clear about setting desiredSize/width/height. That caution applies to the main element of an “Auto” Panel, not to that Panel as a whole. It is commonplace to specify the size of a Panel. But it is the responsibility of the “Auto” Panel to set the size of its main element. GoJS Panels -- Northwoods Software

But even if you set the size of the port’s Rectangle Shape, I do not think that would cause the link connection point offset that you are seeing. (You might see that rectangle clipped or extra space around it.)

What is your link template? Have you customized routing somehow, perhaps in a layout?

This is my current makePort function -

function makePort(name, spot) {
  return $(
    go.Panel,
    'Auto',
    {
      alignment: spot,
      alignmentFocus: spot,
      fromSpot: spot,
      toSpot: spot,
      cursor: 'pointer',
      fromLinkable: true,
      toLinkable: true,
      opacity: 0,
    },
    new go.Binding('portId', 'key', key => `${name}::${key}`),
    $(go.Shape, 'Rectangle', {
      fill: lighten(common.white, 0.1),
      stroke: null,
      desiredSize: new go.Size(10, 10),
    }),
    $(go.Shape, 'XLine', {
      name: 'x',
      fill: null,
      alignment: go.Spot.Center,
      stroke: blueGrey[300],
      desiredSize: new go.Size(5, 5),
    })
  );
}

The link template used here -

const linkTemplate = $(
  go.Link,
  {
    routing: go.Link.Orthogonal,
    curve: go.Link.JumpGap,
    corner: 5,
    reshapable: true,
    resegmentable: true,
    toShortLength: 10,
    relinkableFrom: true,
    relinkableTo: true,
    selectable: true,
    selectionAdornmentTemplate: linkSelectionAdornment,
  },
  bindings.linkPoints,
  $(
    go.Shape, // the link shape
    {
      cursor: 'pointer',
      isPanelMain: true,
      strokeWidth: 10,
      stroke: 'transparent',
    }
  ),
  $(
    go.Shape, // the link shape
    {
      isPanelMain: true,
      strokeWidth: 2,
    },
    bindings.linkStroke,
    bindings.strokeType
  ),
  $(
    go.Shape, // the arrow head
    {
      toArrow: 'standard',
      stroke: null,
      scale: 1.5,
      fill: blue[700],
    },
    bindings.linkFill
  ),
  $(
    go.Panel, // panel for link label
    'Auto',
    {
      segmentFraction: 0.5,
    },
    bindings.linkLabelVisible,
    $(go.Shape, {
      fill: common.white,
      stroke: grey[200],
    }),
    $(
      go.TextBlock, // label text
      {
        margin: 10,
        editable: true,
      },
      bindings.label
    )
  ),
  {
    toolTip: $(
      // link tooltip
      go.Adornment,
      'Auto',
      {
        shadowVisible: true,
        shadowColor: blueGrey[200],
        isShadowed: true,
        shadowBlur: 1,
        shadowOffset: new go.Point(3, 3),
      },
      bindings.linkTooltipVisible,
      $(go.Shape, 'RoundedRectangle', {
        fill: common.white,
        stroke: null,
      }),
      $(
        go.TextBlock,
        {
          margin: new go.Margin(10),
          shadowVisible: false,
        },
        bindings.text
      )
    ),
  }
);

The points bindings used above -

const parsePoints = pointsString => {
  const points = JSON.parse(pointsString);

  const list = new go.List();
  for (let i = 0; i < points.length; i += 2) {
    const x = parseFloat(points[i]);
    const y = parseFloat(points[i + 1]);
    list.add(new go.Point(x, y));
  }

  return list;
};

const stringifyPoints = pointsObj => {
  const iterator = pointsObj.iterator;
  const points = [];
  while (iterator.next()) {
    const point = iterator.value;
    points.push(point.x);
    points.push(point.y);
  }

  return JSON.stringify(points);
};

const bindings = {
  get linkPoints() {
    return new go.Binding('points', LINK_MODEL_ATTRS.POINTS, parsePoints).makeTwoWay(
      stringifyPoints
    );
  }
};

I don’t see in that code any cause for your connection offset problem.

What about Diagram.layout and any event listeners?

Diagram layout is default. Haven’t done anything there. The group that houses those nodes have custom layout setup which is a combination of what we discussed in the following thread -

What type of event listeners might cause this? Any idea which one should I look at?

I think the problem is that you are using the same Spot value for both alignment and from/toSpot. You want the fromSpot and toSpot to always be a center value.

So how do I do that?

It’s your code – the calls to makePort.

Yea but I have tried go.Spot.Center there. It still does the same thing.

Where? How? With what effect?

Nope. Sorry my bad. It doesn’t do the same thing. If I change toSpot and fromSpot - go.Spot.Center. It does this -

The links are starting from the left most part and ending there as well.

By “center” I meant the middle of the appropriate side.

By the way, what version of GoJS are you using?

Yea. But how do I do that? Which value will do that?

Version - 2.1.8