DynamicPorts example in React

Hello!

I am evaluating GoJS by trying to recreate some in-house stuff using code based on the Dynamic Ports sample, which is the closest to our use case.

Easiest way for me to do this is to put the code into our React app on a separate page, so I’ve done exactly that. I merged the React sample with the Dynamic Ports sample, which required a few adaptions but was straight forward. Migrating our objects into nodeDataArray was very easy using a few new go.Bindings.

But when I got the node ports created I see my sample is not behaving as the Dynamic Ports sample. It looks like it uses the default link instead of the CustomLink in the code. I can drag a new link from one port to another, and it snaps to the ports just fine. But when I release the mouse the created link does not go between the ports I pointed to, it goes go from node center to node center.

There are three pieces of code that are involved AFAICS:

...
myDiagram.linkTemplate = $( CustomLink,  // defined below
	{
		routing: go.Link.AvoidsNodes,
		corner: 4,
		curve: go.Link.JumpGap,
		reshapable: true,
		resegmentable: true,
		relinkableFrom: true,
		relinkableTo: true
	},
	new go.Binding( "points" ).makeTwoWay(),
	$( go.Shape, { stroke: "#2F4F4F", strokeWidth: 2 } )
)
...
function CustomLink() {
	go.Link.call( this )
}

go.Diagram.inherit( CustomLink, go.Link )
...

I have verified that CustomLink() itself gets called when the diagram is created, but then something is missing in the inheritance because findSidePortIndexAndCount is never called when I create links.

Any tips on how to get this hooked up right would be helpful!

(Full code at https://pastebin.com/DNgNpWkU)

Ok, I read the docs at GoJS Ports in Nodes-- Northwoods Software and implemented linkFromPortIdProperty etc. Then the ports works fine.

Good – you actually did read the documentation and were able to solve that problem on your own.

Regarding making use of the CustomLink class, you have to make sure that the link template uses your CustomLink class instead of the standard go.Link class.

Yes I made CustomLinks work as well, but they are not pretty. I tried some improvements to the algorithm, but I am not understanding well how the results of computeEndSegmentLength are actually used because it seems rather random, sometimes the angled line comes in at this distance and sometimes another, and sometimes it makes a little loop :-(

Are there any better solutions to this situation:

Those loops happen because the nodes are too close to each other, so there isn’t room for the links to be routed in the normal manner for that Link class.

If you adjust the routing:

          var info = this.findSidePortIndexAndCount(node, port);
          var idx = info[0];
          var count = info[1];
          if (port._side == "top" || port._side == "bottom") {
            if (otherpt.x < thispt.x) {
              return 2 + idx * 4;
            } else {
              return 4 + (count - idx - 1) * 4;
            }
          } else {  // left or right
            if (otherpt.y < thispt.y) {
              return 2 + idx * 4;
            } else {
              return 4 + (count - idx - 1) * 4;
            }
          }

I get this:
image

If this is not the kind of routing that you wanted, please describe it.

That is pretty good, the result is certainly OK when releasing a node after moving it.
But during the drag the rubber-bands are flashy and jump around a lot, making it hard to see what the final result will be.

We are just doing an evaluation here, so no need to waste time improving at this point.

But I’d like to ask: Should we decide to purchase a license, is this a thing you would be willing to support us with solving?

Yes, although we can never guarantee satisfying every wish.

Ok, so now that we do have support I would like to get back to this topic ;-)
What do I need to adjust to get rid of the loops?

Could you please explain the situation? I do not know what you have and how it has changed since this topic started.

I would like to get rid of this type of loops:

bild

I am using the code proposed by you earlier:

class CustomLink extends go.Link {
	constructor() {
		super()
	}

	findSidePortIndexAndCount( node, port ) {
		if( node.data.cfObject !== null ) {
			const side = port._side
			const arr = node.data[ side + "Array" ]
			const len = arr.length
			for( let i = 0; i < len; i++ ) {
				if( arr[ i ].portId === port.data.portId ) return [ i, len ]
			}
			console.log( 'Port not found!', node.data, port.data )
			return [ -1, len ]
		}
	}

	computeEndSegmentLength( node, port, spot, from ) {
		const esl = go.Link.prototype.computeEndSegmentLength.call( this, node, port, spot, from )
		const other = this.getOtherPort( port )
		if( port !== null && other !== null ) {
			const thisPoint = port.getDocumentPoint( this.computeSpot( from ) )
			const otherPoint = other.getDocumentPoint( this.computeSpot( !from ) )
			if( Math.abs( thisPoint.x - otherPoint.x ) > 20 || Math.abs( thisPoint.y - otherPoint.y ) > 20 ) {
				const info = this.findSidePortIndexAndCount( node, port )
				const idx = info[ 0 ]
				const count = info[ 1 ]
				if( port._side === "top" || port._side === "bottom" ) {
					if( otherPoint.x < thisPoint.x ) {
						return 2 + idx * 4
					} else {
						return 4 + ( count - idx - 1 ) * 4
					}
				} else {  // left or right
					if( otherPoint.y < thisPoint.y ) {
						return 2 + idx * 4
					} else {
						return 4 + ( count - idx - 1 ) * 4
					}
				}
			}
		}
		return esl
	}

	hasCurviness() {
		if( isNaN( this.curviness ) ) return true
		return go.Link.prototype.hasCurviness.call( this )
	}

	computeCurviness() {
		if( isNaN( this.curviness ) ) {
			const { fromNode, toNode, fromPort, toPort } = this
			const fromSpot = this.computeSpot( true )
			const fromPoint = fromPort.getDocumentPoint( fromSpot )
			const toSpot = this.computeSpot( false )
			const toPoint = toPort.getDocumentPoint( toSpot )
			if( Math.abs( fromPoint.x - toPoint.x ) > 20 || Math.abs( fromPoint.y - toPoint.y ) > 20 ) {
				if( ( fromSpot.equals( go.Spot.Left ) || fromSpot.equals( go.Spot.Right ) ) &&
					( toSpot.equals( go.Spot.Left ) || toSpot.equals( go.Spot.Right ) ) ) {
					const fromSegmentLength = this.computeEndSegmentLength( fromNode, fromPort, fromSpot, true )
					const toSegmentLength = this.computeEndSegmentLength( toNode, toPort, toSpot, false )
					const c = ( fromSegmentLength - toSegmentLength ) / 2
					if( fromPoint.x + fromSegmentLength >= toPoint.x - toSegmentLength ) {
						if( fromPoint.y < toPoint.y ) return c
						if( fromPoint.y > toPoint.y ) return -c
					}
				} else if( ( fromSpot.equals( go.Spot.Top ) || fromSpot.equals( go.Spot.Bottom ) ) &&
					( toSpot.equals( go.Spot.Top ) || toSpot.equals( go.Spot.Bottom ) ) ) {
					const fromSegmentLength = this.computeEndSegmentLength( fromNode, fromPort, fromSpot, true )
					const toSegmentLength = this.computeEndSegmentLength( toNode, toPort, toSpot, false )
					const c = ( fromSegmentLength - toSegmentLength ) / 2
					if( fromPoint.x + fromSegmentLength >= toPoint.x - toSegmentLength ) {
						if( fromPoint.y < toPoint.y ) return c
						if( fromPoint.y > toPoint.y ) return -c
					}
				}
			}
		}
		return go.Link.prototype.computeCurviness.call( this )
	}
}

Try decreasing the magnitude of the computed end segment length – I suggest reducing the multiplier of 4 to 3 or 2. Maybe also reduce the 2+ and 4+ to smaller numbers, perhaps zero and 2+, respectively.

[EDIT] The problem might be that the value returned by computeCurviness is too large given how close the end segments are to each other. I think this is a bug, so we’ll investigate it.

OK, I think we have fixed this bug for version 2.1.30, which should be coming out within a few days, unless we find another bug…

1 Like

Could you try 2.1.30 now, please? I’d like to know if the results are better for you.

Yes the new version combined with some tweaking of the factors above has improved the situation significantly.