I have a diagram using a modified LayeredDigraphLayout. I need 5 layers to be displayed at all times, even when a layer does not contain any nodes.
This is the overridden assignLayers() function of the custom layout (there are no other modifications):
assignLayers = (): void => {
const layout = this as DeviceEditWiringViewDiagramLayout;
// call super for initial layer assignments
DeviceEditWiringViewDiagramLayout.prototype.assignLayers.call(layout);
// iterate through all the nodes
layout.network.vertexes.each((v: go.LayeredDigraphVertex) => {
// we need to check the node template to be "GeneralDevice", as only its data
// model is the type of DeviceHeaderDiagramModel
switch (v.node.category) {
case "GeneralDevice":
const model = v.node.data as DeviceHeaderDiagramModel;
// nodes without input ports go to the left side
if (model.ports.every(group => group.inputPorts.length === 0)) {
v.layer = 4;
}
// the only node with both input and output ports goes to the middle
else if (model.ports.some(
group =>
group.inputPorts.length !== 0 &&
group.outputPorts.length !== 0
)) {
v.layer = 2;
}
// nodes without output ports go to the right
else if (model.ports.every(group => group.outputPorts.length === 0)) {
v.layer = 0;
}
break;
case "GeneralDevicePlaceholder":
const placeholderModel = v.node.data as DeviceHeaderPlaceholderDiagramModel;
if (placeholderModel.inputPort) v.layer = 1;
else if (placeholderModel.outputPort) v.layer = 3;
break;
default:
break;
}
});
};
So I explicitly tell every node on the diagram which layer they should be in and this is working correctly. The issue is if one of the middle layers (second and fourth) is empty (does not contain any nodes), the outer layers (first and fifth) jump in their place (0->1 and 4-> 3).
This is how it looks like correctly with 5 layers:
When there are no vertexes in a layer, the layer takes zero space.
You can create a dummy vertex in such layers (or all layers, really), and override LayeredDigraphLayout.nodeMinLayerSpace to return whatever value you like.
function LDL() {
go.LayeredDigraphLayout.call(this);
}
go.Diagram.inherit(LDL, go.LayeredDigraphLayout);
LDL.prototype.assignLayers = function() {
go.LayeredDigraphLayout.prototype.assignLayers.call(this);
. . .
// if there aren't any vertexes in layer 1, create a dummy one:
var v = this.network.createVertex();
v.layer = 1;
this.network.addVertex(v);
}
LDL.prototype.nodeMinLayerSpace = function(v, topleft) {
if (v.layer === 1) return 50;
return go.LayeredDigraphLayout.prototype.nodeMinLayerSpace.call(this, v, topleft);
}
assignLayers = (): void => {
const layout = this as DeviceEditWiringViewDiagramLayout;
// call super for initial layer assignments
DeviceEditWiringViewDiagramLayout.prototype.assignLayers.call(layout);
// we will count the number of nodes in each layer here
const layerNodeCounts = new Array<number>();
// iterate through all the nodes
layout.network.vertexes.each((v: go.LayeredDigraphVertex) => {
// we need to check the node template to be "GeneralDevice", as only its data
// model is the type of DeviceHeaderDiagramModel
switch (v.node.category) {
case "GeneralDevice":
const model = v.node.data as DeviceHeaderDiagramModel;
// nodes without ports go to the left side
if (model.ports.every(group => group.inputPorts.length === 0)) {
v.layer = 4;
}
// the only node with both input and output ports goes to the middle
else if (model.ports.some(
group =>
group.inputPorts.length !== 0 &&
group.outputPorts.length !== 0
)) {
v.layer = 2;
}
// nodes without output ports go to the right
else if (model.ports.every(group => group.outputPorts.length === 0)) {
v.layer = 0;
}
break;
case "GeneralDevicePlaceholder":
const placeholderModel = v.node.data as DeviceHeaderPlaceholderDiagramModel;
if (placeholderModel.inputPort) v.layer = 1;
else if (placeholderModel.outputPort) v.layer = 3;
break;
default:
break;
}
// add 1 to the layer count or set it to 1 if it was undefined
layerNodeCounts[v.layer] ? layerNodeCounts[v.layer]++ : layerNodeCounts[v.layer] = 1;
});
for (let i = 0; i < layerNodeCounts.length; i++) {
// if there aren't any vertexes in a given layer, create a dummy one
if (!layerNodeCounts[i]) {
var v = layout.network.createVertex() as go.LayeredDigraphVertex;
v.layer = i;
layout.network.addVertex(v);
}
}
};
nodeMinLayerSpace = (v: go.LayeredDigraphVertex, topleft: boolean): number => {
// this is needed for the dummy nodes
// as nodeMinLayerSpace calculates from the center, we will take the half of the standard node width
return Constants.diagram.nodeDefaultWidth / 2;
}
I suggest that you blindly add a dummy vertex to each layer and mark them so that you can recognize them in your override of nodeMinLayerSpace to return what you want, or else return the super call.
Okey, fair, the leftmost layer is the last in the array. If it was empty, I wasn’t even getting there in the for loop to add a dummy vertex. I have fixed the number of layers (5) to a constant and it is working correctly now. Thank you a lot!
for (let i = 0; i < NUMBER_OF_LAYERS; i++) {
// if there aren't any vertexes in a given layer, create a dummy one
if (!layerNodeCounts[i]) {
var v = layout.network.createVertex() as go.LayeredDigraphVertex;
v.layer = i;
layout.network.addVertex(v);
console.log("dummy vertex added to layer", i);
}
}