Hi There!
I have my diagram:
that order here by horizontal.
and here it sorted by vertical:
I want to display bands in horizontal and vertical and arrange the nodes in some a matrix - such every node get band (x,y) and not just x or just y.
my code:
public initDiagram(): go.Diagram {
// this controls whether the layout is horizontal and the layer bands are vertical, or vice-versa:
let HORIZONTAL = this.horizontal; // this constant parameter can only be set here, not dynamically
// Perform a TreeLayout where the node's actual tree-layer is specified by the "band" property on the node data.
// This implementation only works when angle == 0, but could be easily modified to support other angles.
function BandedTreeLayout() {
go.TreeLayout.call(this);
this.treeStyle = go.TreeLayout.StyleLayered; // required
// new in GoJS v1.4
this.layerStyle = go.TreeLayout.LayerUniform;
// don't move subtrees closer together, to maintain possible empty spaces between layers
this.compaction = go.TreeLayout.CompactionNone;
// move the parent node towards the top of its subtree area
this.alignment = go.TreeLayout.AlignmentStart;
// sort a parent's child vertexes by the value of the index property
function compareIndexes(v, w) {
var vidx = v.index;
if (vidx === undefined) vidx = 0;
var widx = w.index;
if (widx === undefined) widx = 0;
return vidx - widx;
}
this.sorting = go.TreeLayout.SortingAscending;
this.comparer = compareIndexes;
//this.setsPortSpot = false;
this.setsChildPortSpot = false;
}
go.Diagram.inherit(BandedTreeLayout, go.TreeLayout);
// Modify the standard LayoutNetwork by making children with the same "band" value as their
// parents actually be children of the grandparent.
BandedTreeLayout.prototype.makeNetwork = function (coll) {
var net = go.TreeLayout.prototype.makeNetwork.call(this, coll);
// add artificial root and link with all root vertexes
var singles = [];
for (var it = net.vertexes.iterator; it.next();) {
var v = it.value;
if (v.node && v.sourceEdges.count === 0) {
singles.push(v);
}
}
if (singles.length > 0) {
var dummyroot = net.createVertex();
net.addVertex(dummyroot);
singles.forEach(function (v) {
net.linkVertexes(dummyroot, v, null);
});
}
// annotate every child with an index, used for sorting
for (var it = net.vertexes.iterator; it.next();) {
var parent = it.value;
var idx = 0;
for (var cit = parent.destinationVertexes; cit.next();) {
var child = cit.value;
child.index = idx;
idx += 10000;
}
}
// now look for children with the same band value as their parent
for (var it = net.vertexes.iterator; it.next();) {
var parent = it.value;
if (!parent.node) continue;
// Should this be recursively looking for grandchildren/greatgrandchildren that
// have the same band as this parent node?? Assume that is NOT required.
var parentband = parent.node.data.band;
var edges = [];
for (var eit = parent.destinationEdges; eit.next();) {
var edge = eit.value;
var child = edge.toVertex;
if (!child.node) continue;
var childband = child.node.data.band;
if (childband <= parentband) edges.push(edge);
}
// for each LayoutEdge that connects the parent vertex with a child vertex
// whose node has the same band #, reconnect the edge with the parent's parent vertex
var grandparent = parent.sourceVertexes.first();
if (grandparent !== null) {
var cidx = 1;
for (var i = 0; i < edges.length; i++) {
var e = edges[i];
parent.deleteDestinationEdge(e);
e.fromVertex = grandparent;
grandparent.addDestinationEdge(e);
var child = e.toVertex;
child.index = parent.index + cidx;
cidx++;
}
}
}
return net;
};
BandedTreeLayout.prototype.assignTreeVertexValues = function (v) {
if (v.node && v.node.data && v.node.data.band) {
v.originalLevel = v.level; // remember tree assignment
v.level = Math.max(v.level, v.node.data.band); // shift down to meet band requirement
}
};
BandedTreeLayout.prototype.commitLayers = function (layerRects, offset) {
// for debugging:
//for (var i = 0; i < layerRects.length; i++) {
// if (window.console) window.console.log(layerRects[i].toString());
//}
for (var it = this.network.vertexes.iterator; it.next();) {
var v = it.value;
var n = v.node;
if (n && v.originalLevel) {
// the band specifies the horizontal position
var diff = n.data.band - v.originalLevel;
if (diff > 0) {
var pos = v.bounds.position;
// this assumes that the angle is zero: rightward growth
HORIZONTAL ? pos.x = layerRects[v.level].x : pos.y = layerRects[v.level].y;
n.move(pos);
}
}
}
// update the background object holding the visual "bands"
var bands = this.diagram.findPartForKey("_BANDS");
if (bands) {
bands.layerRects = layerRects; // remember the Array of Rect
var model = this.diagram.model;
for (var it = this.network.vertexes.iterator; it.next();) {
var v = it.value;
if (!v.node) continue;
model.setDataProperty(v.node.data, "band", v.level);
}
bands.location = this.arrangementOrigin.copy().add(offset);
var arr = bands.data.itemArray;
for (var i = 0; i < layerRects.length; i++) {
var itemdata = arr[i];
if (itemdata) {
model.setDataProperty(itemdata, "bounds", layerRects[i]);
}
}
}
};
const $ = go.GraphObject.make;
const dia = $(go.Diagram, {
// @ts-ignore
layout: $(BandedTreeLayout, // custom layout is defined above
{
angle: HORIZONTAL ? 0 : 90,
arrangement: HORIZONTAL ? go.TreeLayout.ArrangementVertical : go.TreeLayout.ArrangementHorizontal
}),
'initialContentAlignment': go.Spot.Center,
'undoManager.isEnabled': true,
model: $(go.GraphLinksModel,
{
linkToPortIdProperty: 'toPort',
linkFromPortIdProperty: 'fromPort',
linkKeyProperty: 'key' // IMPORTANT! must be defined for merges and data sync when using GraphLinksModel
}
),
});
dia.nodeTemplateMap.add("Bands",
$(go.Part, "Position",
new go.Binding("itemArray"),
{
isLayoutPositioned: false, // but still in document bounds
locationSpot: new go.Spot(0, 0, HORIZONTAL ? 0 : 16, HORIZONTAL ? 16 : 0), // account for header height
layerName: "Background",
pickable: false,
selectable: false,
itemTemplate:
$(go.Panel, HORIZONTAL ? "Vertical" : "Horizontal",
new go.Binding("opacity", "visible", function (v) {
return v ? 1 : 0;
}),
new go.Binding("position", "bounds", function (b) {
return b.position;
}),
$(go.TextBlock,
{
angle: HORIZONTAL ? 0 : 270,
textAlign: "center",
wrap: go.TextBlock.None,
font: "bold 11pt sans-serif",
background: $(go.Brush, "Linear", {0: "aqua", 1: go.Brush.darken("aqua")})
},
new go.Binding("text"),
// always bind "width" because the angle does the rotation
new go.Binding("width", "bounds", function (r) {
return HORIZONTAL ? r.width : r.height;
})
),
// option 1: rectangular bands:
$(go.Shape,
{stroke: null, strokeWidth: 0},
new go.Binding("desiredSize", "bounds", function (r) {
return r.size;
}),
new go.Binding("fill", "itemIndex", function (i) {
return i % 2 == 0 ? "whitesmoke" : go.Brush.darken("whitesmoke");
}).ofObject())
// option 2: separator lines:
//(HORIZONTAL
// ? $(go.Shape, "LineV",
// { stroke: "gray", alignment: go.Spot.TopLeft, width: 1 },
// new go.Binding("height", "bounds", function(r) { return r.height; }),
// new go.Binding("visible", "itemIndex", function(i) { return i > 0; }).ofObject())
// : $(go.Shape, "LineH",
// { stroke: "gray", alignment: go.Spot.TopLeft, height: 1 },
// new go.Binding("width", "bounds", function(r) { return r.width; }),
// new go.Binding("visible", "itemIndex", function(i) { return i > 0; }).ofObject())
//)
)
}
));
nodes exmaple:
nodes: [{category: “Bands”, itemArray: [{visible: “false”}, {text: “0”}, {text: “(0 1)”}, {text: “1”}],…},…]
0: {category: “Bands”, itemArray: [{visible: “false”}, {text: “0”}, {text: “(0 1)”}, {text: “1”}],…}
1: {band: 1, category: “simple”, key: “Q0”,…}
2: {band: 1, category: “simple”, key: “Q1”,…}
3: {band: 1, category: “simple”, key: “Q2”,…}
4: {band: 2, category: “simple”, key: “Q3”,…}
. 5: {band: 3, category: “simple”, key: “Q4”,…}
6: {band: 3, category: “simple”, key: “Q5”,…}
7: {band: 3, category: “simple”, key: “Q6”,…}
Thank you so much!!
BR,
Amit.