Link node to its closest port

Hi,
I want to link my node from link to its closest port while auto arranging the layout right now i am using this layout

//#region LayeredDigraphLayout

function ArrangingLDLayout() {
    go.LayeredDigraphLayout.call(this);
    this._singletons = null;
}
go.Diagram.inherit(ArrangingLDLayout, go.LayeredDigraphLayout);

ArrangingLDLayout.prototype.makeNetwork = function (coll) {
    var net = go.LayeredDigraphLayout.prototype.makeNetwork.call(this, coll);
    // delete all vertexes that have no edges
    var singletons = new go.Set();
    net.vertexes.each(function (v) {
        if (v.edgesCount === 0 && v.node !== null) singletons.add(v.node);
    });
    singletons.each(function (n) {
        net.deleteNode(n);
    });
    this._singletons = singletons;  // remember for commitLayout
    return net;
};

ArrangingLDLayout.prototype.commitLayout = function () {
    go.LayeredDigraphLayout.prototype.commitLayout.call(this);
    var p = this.arrangementOrigin.copy();
    p.x += this.columnSpacing / 2;  // horizontal indent
    this.network.vertexes.each(function (v) {
        var n = v.node;
        if (n === null) return;
        p.y = Math.max(p.y, n.actualBounds.bottom);
    });
    p.y += 50; // vertical spacing
    var hspace = this.columnSpacing;
    this._singletons.each(function (n) {
        n.move(p);
        p.x += n.actualBounds.width + 50;  // horizontal spacing
    });
};

//#endregion

and i want to link my nodes to its closest port when i apply this layout to my model

the link coming from ( A -> D ) is not linking for top its get link to right side i want to link to the closest port so that my link layout will get improved please let me know how would it is possible for node creation i am using this code

to create Port

function makePort(id, align, spot) {
var port = (go.Shape, { fill: "transparent", stroke: null, portId: id, alignment: align, cursor: "pointer", fromSpot: spot, fromLinkable: true, fromLinkableDuplicates: true, fromLinkableSelfNode: true, toSpot: spot, toLinkable: true, toLinkableSelfNode: true, toLinkableDuplicates: true, mouseEnter: portMouseEnter, mouseLeave: portMouseLeave, toolTip: // define a tooltip for each node that displays the color as text (go.Adornment, “Auto”,
(go.Shape, { fill: "#FFFFCC" }), (go.TextBlock, { margin: 4 },
new go.Binding(“text”, “”, diagramInfo))
) // end of Adornment
});
if (align.equals(go.Spot.Top) || align.equals(go.Spot.Bottom)) {
port.height = 3;
port.stretch = go.GraphObject.Horizontal;
} else {
port.width = 3;
port.stretch = go.GraphObject.Vertical;
}
return port;
}

and for node

var nodeResizeAdornmentTemplate =
(go.Adornment, "Spot", { locationSpot: go.Spot.Right }, (go.Placeholder),
(go.Shape, { alignment: go.Spot.TopLeft, cursor: "nw-resize", desiredSize: new go.Size(6, 6), fill: "lightblue", stroke: "deepskyblue", maxSize: new go.Size(250, 150) }), (go.Shape, { alignment: go.Spot.Top, cursor: “n-resize”, desiredSize: new go.Size(6, 6), fill: “lightblue”, stroke: “deepskyblue”, maxSize: new go.Size(250, 150) }),
$(go.Shape, { alignment: go.Spot.TopRight, cursor: “ne-resize”, desiredSize: new go.Size(6, 6), fill: “lightblue”, stroke: “deepskyblue”, maxSize: new go.Size(250, 150) }),

       $(go.Shape, { alignment: go.Spot.Left, cursor: "w-resize", desiredSize: new go.Size(6, 6), fill: "lightblue", stroke: "deepskyblue", maxSize: new go.Size(250, 150) }),
       $(go.Shape, { alignment: go.Spot.Right, cursor: "e-resize", desiredSize: new go.Size(6, 6), fill: "lightblue", stroke: "deepskyblue", maxSize: new go.Size(250, 150) }),

       $(go.Shape, { alignment: go.Spot.BottomLeft, cursor: "se-resize", desiredSize: new go.Size(6, 6), fill: "lightblue", stroke: "deepskyblue", maxSize: new go.Size(250, 150) }),
       $(go.Shape, { alignment: go.Spot.Bottom, cursor: "s-resize", desiredSize: new go.Size(6, 6), fill: "lightblue", stroke: "deepskyblue", maxSize: new go.Size(250, 150) }),
       $(go.Shape, { alignment: go.Spot.BottomRight, cursor: "sw-resize", desiredSize: new go.Size(6, 6), fill: "lightblue", stroke: "deepskyblue", maxSize: new go.Size(250, 150) })
     );
    var nodeShape = "RoundedRectangle";
    myDiagram.nodeTemplate =
      $(go.Node, "Auto",
        { layoutConditions: go.Part.LayoutAdded | go.Part.LayoutRemoved, isLayoutPositioned: true, zOrder: 2 },
         new go.Binding("location", "loc", go.Point.parse).makeTwoWay(go.Point.stringify),
         { resizable: false, resizeObjectName: "PANEL", resizeAdornmentTemplate: nodeResizeAdornmentTemplate, desiredSize: new go.Size(108, 67), minSize: new go.Size(108, 67), maxSize: new go.Size(220, 120) },
         new go.Binding("angle").makeTwoWay(),
         new go.Binding("position", "position", go.Point.parse).makeTwoWay(go.Point.stringify),
         new go.Binding("desiredSize", "size", go.Size.parse).makeTwoWay(go.Size.stringify),
        $(go.Shape, nodeShape,
          {
              portId: "",
              parameter1: 2,
              fill: nodebackground, stroke: nodeStroke, strokeWidth: 2,
              spot1: new go.Spot(0, 0, 1, 1),
              spot2: new go.Spot(1, 1, -1, 0),
              minSize: new go.Size(95, 59),
              maxSize: new go.Size(220, 120),
          },
          new go.Binding("strokeWidth", "isHighlighted", function (s, obj) {
              
          }).ofObject(),
            new go.Binding("stroke", "isHighlighted", function (s, obj) {
                if (s && $scope.pathHighlight) { return "#bf31d8"; } else { if (obj.part.data.hasOwnProperty('AssociatedID')) { return "gray"; } else { return nodeStroke; } }
            }).ofObject(),
            new go.Binding("figure", "figure").makeTwoWay(),
            new go.Binding("fill", "fill").makeTwoWay(),
            new go.Binding("stroke", "stroke").makeTwoWay()
          ),
        $(go.Panel, "Vertical",
          {
              alignment: go.Spot.Top,
              stretch: go.GraphObject.Horizontal,
              minSize: new go.Size(108, 67)
          },
          $(go.Panel, "Table",
            {
                background: "lightblue",
                stretch: go.GraphObject.Horizontal,
                width: 15.5, height: 13,
                margin: new go.Margin(2, 3, 0, 3)
            },
                $(go.Shape, "StopSign", {
                    alignment: go.Spot.TopLeft, margin: 2,
                    fill: "red", width: 8, height: 8, stroke: null,
                    visible: visible
                }, new go.Binding("visible", "visible").makeTwoWay()),
                $("Button", {
                    alignment: go.Spot.Right,
                    width: 16,
                    height: 15,
                    margin: new go.Margin(0, 0, 0, 90),
                    click: add_IP_OP,
                    visible: false,
                    cursor: "pointer"

                }, new go.Binding("visible", "stroke", function (color) {
                    if (color != "#00b9ff") {
                        if (color == "gray" && MbtDiagramInstance.getModelTitleName != "Published Model")
                            return true;
                        else
                            return false;
                    }
                }),
                     $(go.TextBlock, {
                         text: '',
                         textAlign: "center",
                         font: '10px FontAwesome'
                     }),
                )),

          $(go.TextBlock,
          {
              font: "12px Arial, sans-serif", //stroke: lightText,
              margin: new go.Margin(4, 6, 4, 6),
              stretch: go.GraphObject.Horizontal, textAlign: "center",
              height: 38,
              maxLines: 3,
               verticalAlignment: go.Spot.Center,
              editable: true, isMultiline: true, //textValidation: validateText,
              textEdited: function (textBlock, previousText, currentText) {
                  
              },
          },
          new go.Binding("text", "text").makeTwoWay())),
          { contextMenu: $(go.Adornment) },
         makePort("T", go.Spot.Top, go.Spot.TopSide),
         makePort("L", go.Spot.Left, go.Spot.LeftSide),
         makePort("R", go.Spot.Right, go.Spot.RightSide),
         makePort("B", go.Spot.Bottom, go.Spot.BottomSide)

      );

So do you want links to come out of nodes at distinct ports, but you want links to go to a node at the closest side?

If so, I suggest that links continue to come from ports as you have now, and that they go to the default port (whose portId is the empty string, and which might be in your case the rectangular body without the ports) and that its toSpot be go.Spot.NotBottomSide.

hey walter for me Default work but now the link which is comping for A->D its not comping straight can you tell me how would i do that too.

and in this given diagram my from ports are coming out from different spot point but when i apply the go.spot.default into tospot then all tospot points are get merged into one how would i fix it.

So have you set the toSpot on your node’s default port to be go.Spot.NotBottomSide? Or would you prefer if the links always came to the top: go.Spot.TopSide?

Here’s what I just tried:

    function init() {
      if (window.goSamples) goSamples();  // init for these samples -- you don't need to call this
      var $ = go.GraphObject.make;  // for conciseness in defining templates

      myDiagram = $(go.Diagram, "myDiagramDiv",
        {
          initialContentAlignment: go.Spot.Center,
          "undoManager.isEnabled": true,
          layout: $(go.LayeredDigraphLayout, { direction: 90, setsPortSpots: false }),
        });

      myDiagram.nodeTemplate =
        $(go.Node, "Auto",
          $(go.Shape,
            { portId: "", fromSpot: go.Spot.BottomSide, toSpot: go.Spot.TopSide },
            { fill: "white" },
            new go.Binding("fill", "color")),
          $(go.TextBlock,
            { margin: 8 },
            new go.Binding("text"))
        );

      myDiagram.linkTemplate =
        $(go.Link,
          { routing: go.Link.Orthogonal, corner: 10 },
          $(go.Shape),
          $(go.Shape, { toArrow: "OpenTriangle" })
        );

      myDiagram.model = new go.GraphLinksModel(
        [
          { key: 1, text: "Alpha", color: "lightblue" },
          { key: 2, text: "Beta", color: "orange" },
          { key: 3, text: "Gamma", color: "lightgreen" },
          { key: 4, text: "Delta", color: "pink" },
          { key: 5, text: "Epsilon", color: "pink" },
          { key: 6, text: "Zeta", color: "pink" },
          { key: 7, text: "Eta", color: "pink" },
          { key: 8, text: "Theta", color: "pink" },
        ],
        [
          { from: 1, to: 3 },
          { from: 1, to: 4 },
          { from: 1, to: 5 },
          { from: 1, to: 6 },
          { from: 1, to: 7 },
          { from: 2, to: 4 },
          { from: 2, to: 5 },
          { from: 2, to: 6 },
          { from: 2, to: 7 },
          { from: 2, to: 8 },
        ]);
    }

The result:

no i would prefer the closest port it can be any

OK, so if I specify:

          $(go.Shape,
            { portId: "", fromSpot: go.Spot.NotTopSide, toSpot: go.Spot.NotBottomSide },

and

        $(go.Link,
          { routing: go.Link.AvoidsNodes, corner: 10 },

I get:

hey walter,
this approach is not working with my view actually i am creating graph and then after create it setting the layout using this code

myDiagram.layout =

            $(ArrangingLDLayout,
                { direction: 90, setsPortSpots: false, layerSpacing: 80, linkSpacing: 10, columnSpacing: 40 }),
        myDiagram.layoutDiagram(true)

so i just want that does it is possible to do this work while setting up the layout

function ArrangingLDLayout() {
    go.LayeredDigraphLayout.call(this);
    this._singletons = null;
}
go.Diagram.inherit(ArrangingLDLayout, go.LayeredDigraphLayout);

ArrangingLDLayout.prototype.makeNetwork = function (coll) {
    var net = go.LayeredDigraphLayout.prototype.makeNetwork.call(this, coll);
    // delete all vertexes that have no edges
    var singletons = new go.Set();
    net.vertexes.each(function (v) {
        if (v.edgesCount === 0 && v.node !== null) singletons.add(v.node);
    });
    singletons.each(function (n) {
        net.deleteNode(n);
    });
    this._singletons = singletons;  // remember for commitLayout
    return net;
};

ArrangingLDLayout.prototype.commitLayout = function () {
    go.LayeredDigraphLayout.prototype.commitLayout.call(this);
    var p = this.arrangementOrigin.copy();
    p.x += this.columnSpacing / 2;  // horizontal indent
    this.network.vertexes.each(function (v) {
        var n = v.node;
        if (n === null) return;
        p.y = Math.max(p.y, n.actualBounds.bottom);
    });
    p.y += 50; // vertical spacing
    var hspace = this.columnSpacing;
    this._singletons.each(function (n) {
        n.move(p);
        p.x += n.actualBounds.width + 50;  // horizontal spacing
    });
};

You don’t need to call Diagram.layoutDiagram if you are changing the Diagram.layout within a transaction.

So what is the problem? I don’t see what is wrong and what you would prefer instead. I see you at least have set LayeredDigraphLayout.setsPortSpots to false, and that’s good. But I don’t know about your other setting on the ports and on the link.

link code

myDiagram.linkTemplate =
$(CustomLink,
{
routing: go.Link.AvoidsNodes,
corner: 5,
adjusting: go.Link.Stretch,
//curve: go.Link.JumpGap,
relinkableFrom: false,
relinkableTo: false,
reshapable: true,
resegmentable: true,
fromLinkable: true,
fromLinkableSelfNode: true,
fromLinkableDuplicates: true,
toLinkable: true,
toLinkableSelfNode: true,
toLinkableDuplicates: true,
zOrder: 1,
selectionAdorned: false,
layoutConditions: go.Part.LayoutAdded | go.Part.LayoutRemoved,
toShortLength: 2,

        },
        new go.Binding("points").makeTwoWay(),
        new go.Binding("zOrder").makeTwoWay(),
        new go.Binding("routing", "routing"),
        new go.Binding("curviness"),
        $(go.Shape, { stroke: "gray", strokeWidth: 1.5, name: "HIGHLIGHT" },
        new go.Binding("strokeWidth", "isHighlighted", function (s, obj) {
            if ($scope.userSelection) {
            } else {
                if (s && $scope.pathHighlight) { return 3 }
                else {
                    if (obj.part.data.hasOwnProperty('AssociatedID') || !obj.part.data.hasOwnProperty('EdgeID')) { return 2 }
                    else { return 2 }
                }
            }
        }).ofObject(),
        new go.Binding("stroke", "isHighlighted", function (s, obj) {
            if ($scope.userSelection) {
            } else {
                if (s && $scope.pathHighlight) { return "#bf31d8"; }
                else {
                    if (obj.part.data.hasOwnProperty('AssociatedID') || !obj.part.data.hasOwnProperty('EdgeID')) { return "gray"; }
                    else { return linkstroke; }
                }
            }
        }).ofObject(),
          new go.Binding("stroke", "stroke").makeTwoWay()),

        $(go.Shape, { fromArrow: "Circle", fill: "gray", strokeWidth: 2, stroke: "gray" }),
        $(go.Shape, { toArrow: 'Standard', fill: "gray", strokeWidth: 2, stroke: "gray" }),
        $(go.Panel, "Auto",
         { _isLinkLabel: true, cursor: "move", },  // marks this Panel as being a draggable label segmentIndex: 2, segmentFraction: 0.8
         new go.Binding("segmentIndex").makeTwoWay(),
         new go.Binding("segmentFraction").makeTwoWay(),
       $(go.Shape, {
           isPanelMain: true,
           fill: linkbackground,
           stroke: linkstroke,
           strokeWidth: 2
       }, new go.Binding("stroke", "stroke").makeTwoWay()),
        $(go.Panel, "Table",
          {
              background: "transparent",
              stretch: go.GraphObject.Horizontal,
          },
      $(go.Shape, "StopSign",
      {
          alignment: go.Spot.TopLeft, margin: 3,
          width: 8, height: 8, fill: "red", visible: visible, stroke: null
      },
     new go.Binding("visible", "visible").makeTwoWay()),
     $("Button", {
         alignment: go.Spot.Right,
         width: 16,
         height: 15,
         click: add_IP_OP,
         visible: false,
         cursor: "pointer",
         margin: go.Margin.parse("0 2 0 0"),
         //margin: new go.Margin(2, 0, 0, 90)

     },
     new go.Binding("visible", "stroke", function (color, eve) {
         var linkIsAssociated = eve.part.data.hasOwnProperty('AssociatedID')
         if ((color == "gray" || color == "dodgerblue") && linkIsAssociated == true && MbtDiagramInstance.getModelTitleName != "Published Model")
             return true;
         else
             return false;
     }),
     $(go.TextBlock, {
         text: '',
         textAlign: "center",
         font: '10px FontAwesome'
     }),
          ),

     new go.Binding('visible', 'text', function (t) { return t.trim() !== '' }),
    $(go.TextBlock, "Label", {
        width: 60,
        height: 16,
        name: "linkLabel",
        margin: go.Margin.parse("4 15 0 9"), editable: true,
        maxLines: 1,
        textAlign: "center",
        isMultiline: true,  // don't allow embedded newlines
        //textValidation: validateText,
        textEdited: function (textBlock, previousText, currentText) {
           }
    },

        new go.Binding('visible', 'text', function (t) {
            return t.trim() !== ''
        }),
            new go.Binding("text", "text", function (e) {
                if (e != "") {
                    return e;
                }
            }).makeTwoWay(),
           ),
            { contextMenu: $(go.Adornment) }
       )
     ));

and creating port

function makePort(id, align, spot) {
var port = $(go.Shape,
{
fill: “transparent”,
stroke: null,
portId: id,
alignment: align,
cursor: “pointer”,
fromSpot: go.Spot.NotTopSide,
fromLinkable: true,
fromLinkableDuplicates: true,
fromLinkableSelfNode: true,
toSpot: go.Spot.NotBottomSide,
toLinkable: true,
toLinkableSelfNode: true,
toLinkableDuplicates: true,
mouseEnter: portMouseEnter,
mouseLeave: portMouseLeave,
toolTip: // define a tooltip for each node that displays the color as text
$(go.Adornment, “Auto”,
$(go.Shape, { fill: “#FFFFCC” }),
$(go.TextBlock, { margin: 4 },
new go.Binding(“text”, “”, diagramInfo))
) // end of Adornment
});
if (align.equals(go.Spot.Top) || align.equals(go.Spot.Bottom)) {
port.height = 3;
port.stretch = go.GraphObject.Horizontal;
} else {
port.width = 3;
port.stretch = go.GraphObject.Vertical;
}
return port;
}

Try not setting Link.adjusting.

And if you have a separate small port for each link at the fromNode, your fromSpot should continue to be either go.Spot.Left or go.Spot.Right, as appropriate for the alignment.