Odd dragging behavior with ContinuousForceDirectedLayout

I’m using Angular with typescript.

This is the layout implementation:

import {
	ChangedEvent,
	Diagram,
	ForceDirectedLayout,
	ForceDirectedVertex,
	Group,
	Part,
	Iterable, DraggingTool, Shape
} from 'gojs'

export class ContinuousForceDirectedLayout extends ForceDirectedLayout {
	public isObserving: boolean;
	public isActive: boolean;

	constructor() {
		super();
		ForceDirectedLayout.call(this);
	}

	isFixed(v: ForceDirectedVertex): boolean {
		return v.node.isSelected;
	}

	doLayout(coll: Diagram | Group | Iterable<Part>): void {
		if (!this.isObserving) {
			this.isObserving = true;
			// cacheing the network means we need to recreate it if nodes or links have been added or removed or relinked,
			// so we need to track structural model changes to discard the saved network.
			const lay = this;
			this.diagram.addModelChangedListener(function(e) {
				// modelChanges include a few cases that we don't actually care about, such as
				// "nodeCategory" or "linkToPortId", but we'll go ahead and recreate the network anyway.
				// Also clear the network when replacing the model.
				if (e.modelChange !== '' ||
					(e.change === ChangedEvent.Transaction && e.propertyName === 'StartingFirstTransaction')) {
					lay.network = null;
				}
			});
		}
		let net = this.network;
		if (net === null) {  // the first time, just create the network as normal
			this.network = net = this.makeNetwork(coll);
		} else {  // but on reuse we need to update the LayoutVertex.bounds for selected nodes
			this.diagram.nodes.each(function(n) {
				const v = net.findVertex(n);
				if (v !== null) {
					v.bounds = n.actualBounds;
				}
			});
		}
		// now perform the normal layout
		ForceDirectedLayout.prototype.doLayout.call(this, coll);
		// doLayout normally discards the LayoutNetwork by setting Layout.network to null;
		// here we remember it for next time
		this.network = net;
	}
}

On the component holding the diagram:

/**
	 * Overrides diagram property DoMouseMove
         * This runs after the diagram is ready
	 */
	overrideDiagramDoMouseMove() {
		const diagram: Diagram = this.graph.diagram;
		// dragging a node invalidates the Diagram.layout, causing a layout during the drag
		diagram.toolManager.draggingTool.doMouseMove = () => {
			DraggingTool.prototype.doMouseMove.call(this);
			if (diagram.toolManager.draggingTool.isActive && !this.freezeGraph) {
				diagram.layout.invalidateLayout();
			}
		}
	}

	/**
	 * Invalidate the diagram layout on SelectionMoved diagram event
	 */
	onSelectionMoved() {
		if (!this.freezeGraph) {
			this.graph.diagram.layout.invalidateLayout();
		}
	}

As you can see in this video, when dragging a node it doesn’t follow the mouse pointer until it finishes moving.

https://drive.google.com/file/d/1kpJMP8UjQ3MwIDvHOliBbbF6Xcgs92Jq/view?usp=sharing

That’s odd. It’s different from the sample: Interactive Force Directed Layout

The overrideDiagramDoMouseMove should be overriding the DraggingTool.doMouseMove method of the DraggingTool, not of the Diagram.

Hey Walter, thanks for your response!

In the sample code, it’s calling the method from the diagram as well:
image

this is my code:
image

I tried using the DraggingTool directly in the if statement but no luck either.

Have you changed selection so that more than just the node under the mouse is selected? Or have you changed ContinuousForceDirectedLayout.isFixed to return true for those vertexes/nodes that are children of the node under the mouse?

You could try setting a log point in ContinuousForceDirectedLayout.doLayout so that you can see how often it is being called, just to make sure that it is indeed being called continually during a drag.

Another possibility – problems with Angular zones. It triggers change detection on every single mousemove event on diagram - GoJS - Northwoods Software (nwoods.com) Although I thought our gojs-angular component deals with that now.

I did a refactor to use the gojs-angular package but it’s still happening.

ContinuousForceDirectedLayout.doLayout is being called as I move the mouse but the Node it’s not moving with it.

Hi. I just created a really quick sample using your implementation of ContinuousForceDirectedLayout and gojs-angular. The nodes appear to drag with the mouse there.

What about my code is different from yours?

Thanks @ryanj

The issue was on this piece of code:

overrideDiagramDoMouseMove() {
		const diagram: Diagram = this.graph.diagram;
		// dragging a node invalidates the Diagram.layout, causing a layout during the drag
		diagram.toolManager.draggingTool.doMouseMove = () => {
			DraggingTool.prototype.doMouseMove.call(diagram.toolManager.draggingTool);
			if (diagram.toolManager.draggingTool.isActive && !this.freezeGraph) {
				diagram.layout.invalidateLayout();
			}
		}
	}

Specifically on this line:

DraggingTool.prototype.doMouseMove.call(diagram.toolManager.draggingTool);

I was using:

DraggingTool.prototype.doMouseMove.call(this);

I’m surprised the TypeScript compiler doesn’t catch that. Might be better to call super, if that is allowed outside of a class declaration.