Dear Walter,
Thank you very much for your advise. To expand/collapse parts of a graph, can I still use “TreeExpanderButton” if the model and layout is not a tree but a graph?
For genogram, Please see my code that use a very lightweight custom layout:
var $$ = go.GraphObject.make; // for conciseness in defining templates, avoid $ due to jQuery
function init() {
myDiagram =
$$(go.Diagram, "myDiagramDiv",
{
initialAutoScale: go.Diagram.Uniform,
initialContentAlignment: go.Spot.Center,
// allow double-click in background to create a new node
"clickCreatingTool.archetypeNodeData": { n: "New Person", "a": ["B", "H", "S"] },
"undoManager.isEnabled": true,
// when a node is selected, draw a big yellow circle behind it
nodeSelectionAdornmentTemplate:
$$(go.Adornment, "Auto",
{ layerName: "Grid" }, // the predefined layer that is behind everything else
$$(go.Shape, "Circle", { fill: "yellow", stroke: null }),
$$(go.Placeholder)
),
layout: // use a custom layout, defined below
$$(GenogramLayout, { direction: 90, layerSpacing: 30, columnSpacing: 40, setsPortSpots: false }) //setsPortSpots de cac link marriage di dung huong
});
// two different node templates, one for each sex,
// named by the category value in the node data object
myDiagram.nodeTemplateMap.add("M", // male
$$(go.Node, "Vertical",
{desiredSize: new go.Size(50, 120)},
$$(go.Panel, "Spot",
$$(go.Picture,
{ margin: 10, width: 49, height: 49, background: "red" },
new go.Binding("source")
),
$$(go.Shape, {
geometryString: "F M0 0 L100 0 L100 100 L0 100 z M5,50 a45,45 0 1,0 90,0 a45,45 0 1,0 -90,0 z",
desiredSize: new go.Size(50, 50),
strokeWidth: 0,
fill: 'white'
},
new go.Binding("fill", "color")
)
),
$$(go.TextBlock,
{ textAlign: "center", maxSize: new go.Size(50, NaN) },
new go.Binding("text", "name"))
));
myDiagram.nodeTemplateMap.add("F", // female
$$(go.Node, "Vertical",
{desiredSize: new go.Size(50, 120)},
$$(go.Panel, "Spot",
$$(go.Picture,
{ margin: 10, width: 49, height: 49, background: "white" },
new go.Binding("source")
),
$$(go.Shape, {
geometryString: "F M0 0 L100 0 L100 100 L0 100 z M5,50 a45,45 0 1,0 90,0 a45,45 0 1,0 -90,0 z",
desiredSize: new go.Size(50, 50),
strokeWidth: 0,
fill: 'white'
}
)
),
$$(go.TextBlock,
{ textAlign: "center", maxSize: new go.Size(50, NaN) },
new go.Binding("text", "name"))
));
myDiagram.linkTemplate = // for parent-child relationships
$$(go.Link,
{
routing: go.Link.Orthogonal, curviness: 15,
layerName: "Background", selectable: false,
fromSpot: go.Spot.Bottom, toSpot: go.Spot.Top
},
$$(go.Shape, { strokeWidth: 2 })
);
myDiagram.linkTemplateMap.add("Marriage", // for marriage relationships
$$(go.Link,
{ layerName: "marry", selectable: false,
//isLayoutPositioned: false, //why not effect to position of node connected with this link?
fromSpot: new go.Spot(1, 0.3), toSpot: new go.Spot(0, 0.3) //go.Spot.LeftSide
},
$$(go.Shape, { strokeWidth: 2, stroke: "blue" })
));
myDiagram.linkTemplateMap.add("childInLaw", // for sonInLaw, daughterInLaw relationships
$$(go.Link,
{ selectable: false},
$$(go.Shape, { strokeWidth: 0})
)
);
jQuery.getJSON("people2.txt", function(jsondata){
setupDiagram(myDiagram, jsondata, 5);
});
}
// create and initialize the Diagram.model given an array of node data representing people
function setupDiagram(diagram, array, focusId) {
diagram.model =
go.GraphObject.make(go.GraphLinksModel,
{ nodeCategoryProperty: "s",
// create all of the nodes for people
nodeDataArray: array.nodeDataArray,
linkDataArray: array.linkDataArray
});
var node = diagram.findNodeForKey(focusId);
if (node !== null) {
diagram.select(node);
}
}
// A custom layout that shows the two families related to a person's parents
function GenogramLayout() {
go.LayeredDigraphLayout.call(this);
this.initializeOption = go.LayeredDigraphLayout.InitDepthFirstIn;
//this.spouseSpacing = 5; // minimum space between spouses
}
go.Diagram.inherit(GenogramLayout, go.LayeredDigraphLayout);
/** @override */
GenogramLayout.prototype.makeNetwork = function(coll) {
// generate LayoutEdges for each parent-child Link
var net = this.createNetwork();
if (coll instanceof go.Diagram) {
this.add(net, coll.nodes, true);
this.add(net, coll.links, true);
} else if (coll instanceof go.Group) {
this.add(net, coll.memberParts, false);
} else if (coll.iterator) {
this.add(net, coll.iterator, false);
}
return net;
};
// internal method for creating LayeredDigraphNetwork where husband/wife pairs are represented
// by a single LayeredDigraphVertex corresponding to the label Node on the marriage Link
GenogramLayout.prototype.add = function(net, coll, nonmemberonly) {
// consider all Nodes in the given collection
var it = coll.iterator;
while (it.next()) {
var node = it.value;
if (!(node instanceof go.Node)) continue;
if (!node.isLayoutPositioned || !node.isVisible()) continue;
if (nonmemberonly && node.containingGroup !== null) continue;
var vertex = net.addNode(node);
}
// now do all Links
it.reset();
while (it.next()) {
var link = it.value;
if (!(link instanceof go.Link)) continue;
if (!link.isLayoutPositioned || !link.isVisible()) continue;
if (nonmemberonly && link.containingGroup !== null) continue;
if (link.category != "Marriage") {
var parent = net.findVertex(link.fromNode);
var child = net.findVertex(link.toNode);
net.linkVertexes(parent, child, link);
}
}
};
/** @override */
GenogramLayout.prototype.commitNodes = function() {
go.LayeredDigraphLayout.prototype.commitNodes.call(this);
// position node connect with link of category "Marriage"
this.network.vertexes.each(function(v) {
var nodesOut = v.node.findNodesOutOf();
var nodeIt = nodesOut.iterator;
while (nodeIt.next()) {
var links = v.node.findLinksTo(nodeIt.value);
//traverse links:
var linkIt = links.iterator;
while (linkIt.next()) {
var link = linkIt.value;
if(link.category == "Marriage"){
//nodeIt.value.position = new go.Point(v.x + v.node.actualBounds.width + this.spouseSpacing, v.y);
nodeIt.value.position = new go.Point(v.x + v.node.actualBounds.width + 5, v.y);
}
}
}
});
};
// end GenogramLayout class
======================
My data:
{ “class”: “go.GraphLinksModel”,
“nodeDataArray”: [
{ “key”: -8,“name”: “Trần Văn Trượng”, “s”:“M”, “color”:“red”, “source”: “36.jpg”, “birthDate”:"", “dieDate”:"", “web”: “”},
{ “key”: -7,“name”: “Trần Văn Đạ”, “s”:“M”, “color”:“red”, “source”: “35.jpg”},
{ “key”: -6,“name”: “Trương Thị Tý”, “s”:“F”, “color”:“red”, “source”: “34.jpg”},
{ “key”: -5,“name”: “Trần Thị Lý”, “s”:“F”, “color”:“red”, “source”: “33.jpg”},
{ “key”: -4,“name”: “Trần Thị Lựu”, “s”:“F”, “color”:“red”, “source”: “32.jpg”},
{ “key”: -3,“name”: “Trần Trọng Đạt”, “s”:“M”, “color”:“red”, “source”: “31.jpg”},
{ “key”: -2,“name”: “Trần Quốc Nguyên”, “s”:“M”, “color”:“red”, “source”: “30.jpg”},
{ “key”: -1,“name”: “Trần Văn Mẫn”, “s”:“M”, “color”:“red”, “source”: “29.jpg”},
{ “key”: 0, “name”: “Trần Văn Cầu”, “s”:“M”, “color”:“red”, “source”: “28.jpg”},
{ “key”: -9,“name”: “Nguyễn Văn Chiểu”, “s”:“M”, “color”:“red”, “source”: “48.jpg”},
{ “key”: -10,“name”: “Nguyễn Thị Cấn”, “s”:“F”, “color”:“red”, “source”: “49.jpg”},
{ “key”: -11,“name”: “Nguyễn Văn Hào”, “s”:“M”, “color”:“red”, “source”: “46.jpg”},
{ “key”: -12,“name”: “Nguyễn Thị Hoa”, “s”:“F”, “color”:“red”, “source”: “47.jpg”},
{ “key”: 1, “name”: “Nguyễn Thị Ngân”, “s”:“F”, “color”:“red”, “source”: “1.jpg”},
{ “key”: 2, “name”: “Trần Bích Ngọc”, “s”:“F”, “color”:“red”, “source”: “2.jpg”},
{ “key”: 3, “name”: “Trần Thị Ánh Tuyết”, “s”:“F”, “color”:“red”, “source”: “3.jpg”},
{ “key”: 4, “name”: “Trần Ngọc Dung”, “s”:“F”, “color”:“red”, “source”: “4.jpg”},
{ “key”: 5, “name”: “Trần Đại Long”, “s”:“M”, “color”:“red”, “source”: “5.jpg”},
{ “key”: 6, “name”: “Trần Bích Hạnh”, “s”:“F”, “color”:“red”, “source”: “6.png”},
{ “key”: 7, “name”: “Hoàng Minh Đức”, “s”:“M”, “color”:“red”, “source”: “7.jpg”} ,
{ “key”: 8, “name”: “Hoàng Minh Hiếu”, “s”:“M”, “color”:“red”, “source”: “8.jpg”} ,
{ “key”: 9, “name”: “Mai Thị Ánh Hồng”, “s”:“F”, “color”:“red”, “source”: “9.jpg”},
{ “key”: 10, “name”: “Mai Bình An”, “s”:“F”, “color”:“red”, “source”: “10.jpg”},
{ “key”: 11, “name”: “Mai Thị Cát Tường”, “s”:“F”, “color”:“red”, “source”: “11.jpg”},
{ “key”: 12, “name”: “Trần T Thanh Tú”, “s”:“F”, “color”:“red”, “source”: “12.jpg”},
{ “key”: 13, “name”: “Đoàn Xuân Tâm”, “s”:“M”, “color”:“red”, “source”: “13.jpg”},
{ “key”: 14, “name”: “Đoàn Xuân Hòa”, “s”:“M”, “color”:“red”, “source”: “14.jpg”},
{ “key”: 15, “name”: “Hoàng Ngọc Lan Tiên”, “s”:“F”, “color”:“red”, “source”: “15.jpg”},
{ “key”: 17,“name”: “Hoàng Hồng Phúc”, “s”:“M”, “color”:“red”, “source”: “17.jpg”},
{ “key”: 18,“name”: “Mai Sỹ Hùng”, “s”:“M”, “color”:“red”, “source”: “18.jpg”},
{ “key”: 19,“name”: “Nguyễn Minh Nhật”, “s”:“M”, “color”:“red”, “source”: “19.jpg”},
{ “key”: 20,“name”: “Bùi Thị Thanh Minh”, “s”:“F”, “color”:“red”, “source”: “20.jpg”},
{ “key”: 21,“name”: “Đoàn Văn Xuân”, “s”:“M”, “color”:“red”, “source”: “21.jpg”},
{ “key”: 22,“name”: “Phạm Thị Hảo”, “s”:“F”, “color”:“red”, “source”: “22.jpg”},
{ “key”: 23,“name”: “Trần Tuyết Vân”, “s”:“F”, “color”:“red”, “source”: “23.jpg”},
{ “key”: 24,“name”: “Trần Thúy Loan”, “s”:“F”, “color”:“red”, “source”: “24.jpg”},
{ “key”: 25,“name”: “Trần Quốc Tuấn”, “s”:“M”, “color”:“red”, “source”: “25.jpg”},
{ “key”: 26,“name”: “Trần Thúy Ngọc”, “s”:“F”, “color”:“red”, “source”: “26.jpg”},
{ “key”: 27,“name”: “Trần Quốc Khánh”, “s”:“M”, “color”:“red”, “source”: “27.jpg”},
{ “key”: 28,“name”: “Trần Văn Tân”, “s”:“M”, “color”:“red”, “source”: “37.jpg”},
{ “key”: 29,“name”: “Trần Thị Tuyết”, “s”:“F”, “color”:“red”, “source”: “38.jpg”},
{ “key”: 30,“name”: “Trần Hồng Sương”, “s”:“F”, “color”:“red”, “source”: “39.jpg”},
{ “key”: 31,“name”: “Lê Văn Hào”, “s”:“M”, “color”:“red”, “source”: “40.jpg”},
{ “key”: 32,“name”: “Nguyễn Văn Trầm”, “s”:“M”, “color”:“red”, “source”: “41.jpg”},
{ “key”: 33,“name”: “Nguyễn Hoàng Đức”, “s”:“M”, “color”:“red”, “source”: “50.jpg”},
{ “key”: 34,“name”: “Nguyễn Hoàng Trung”, “s”:“M”, “color”:“red”, “source”: “51.jpg”}
],
“linkDataArray”: [
{ “from”: -8, “to”: -7},
{ “from”: -7, “to”: -5},
{ “from”: -7, “to”: -4},
{ “from”: -7, “to”: -3},
{ “from”: -7, “to”: -2},
{ “from”: -7, “to”: -1},
{ “from”: -7, “to”: 0},
{ “from”: -3, “to”: 28},
{ “from”: -3, “to”: 29},
{ “from”: -3, “to”: 30},
{ “from”: -3, “to”: 2},
{ “from”: -3, “to”: 3},
{ “from”: -3, “to”: 4},
{ “from”: -3, “to”: 5},
{ “from”: -3, “to”: 6},
{ “from”: 2, “to”: 7},
{ “from”: 2, “to”: 8},
{ “from”: 3, “to”: 9},
{ “from”: 3, “to”: 10},
{ “from”: 3, “to”: 11},
{ “from”: 5, “to”: 12},
{ “from”: 6, “to”: 13},
{ “from”: 6, “to”: 14},
{ “from”: 7, “to”: 15},
{ “from”: -2, “to”: 24},
{ “from”: -2, “to”: 25},
{ “from”: -2, “to”: 26},
{ “from”: 30, “to”: 33},
{ “from”: 30, “to”: 34},
{ “from”: -2, “to”: 27},
{ “from”: -9, “to”: 1},
{ “from”: -11, “to”: 19},
{ “from”: -9, “to”: -10, “category”:“Marriage”},
{ “from”: 2, “to”: 17, “category”:“Marriage”},
{ “from”: 3, “to”: 18, “category”:“Marriage”},
{ “from”: 4, “to”: 19, “category”:“Marriage”},
{ “from”: 5, “to”: 20, “category”:“Marriage”},
{ “from”: 6, “to”: 21, “category”:“Marriage”},
{ “from”: 7, “to”: 22, “category”:“Marriage”},
{ “from”: -3, “to”: 1, “category”:“Marriage”},
{ “from”: -7, “to”: -6, “category”:“Marriage”},
{ “from”: -2, “to”: 23, “category”:“Marriage”},
{ “from”: 29, “to”: 31, “category”:“Marriage”},
{ “from”: 30, “to”: 32, “category”:“Marriage”},
{ “from”: -11, “to”: -12, “category”:“Marriage”},
{ “from”: -9, “to”: -3, “category”: “childInLaw”},
{ “from”: -11, “to”: 4, “category”: “childInLaw”}
]
}
======================
Another way not need a custom layout, but generations are layered correctly from top to bottom:
My code:
var $$ = go.GraphObject.make; // for conciseness in defining templates, avoid $ due to jQuery
function init() {
myDiagram =
$$(go.Diagram, "myDiagramDiv",
{
initialAutoScale: go.Diagram.Uniform,
initialContentAlignment: go.Spot.Center,
// allow double-click in background to create a new node
"clickCreatingTool.archetypeNodeData": { n: "New Person", "a": ["B", "H", "S"] },
"undoManager.isEnabled": true,
// when a node is selected, draw a big yellow circle behind it
nodeSelectionAdornmentTemplate:
$$(go.Adornment, "Auto",
{ layerName: "Grid" }, // the predefined layer that is behind everything else
$$(go.Shape, "Circle", { fill: "yellow", stroke: null }),
$$(go.Placeholder)
),
layout: // use a custom layout, defined below
$$(go.LayeredDigraphLayout, { direction: 90, layerSpacing: 30, columnSpacing: 10, setsPortSpots: false }) //setsPortSpots de cac link marriage di dung huong
});
// two different node templates, one for each sex,
// named by the category value in the node data object
myDiagram.nodeTemplateMap.add("M", // male
$$(go.Node, "Vertical",
{desiredSize: new go.Size(50, 120)},
$$(go.Panel, "Spot",
$$(go.Picture,
{ margin: 10, width: 49, height: 49, background: "red"
},
new go.Binding("source")
),
$$(go.Shape, {
geometryString: "F M0 0 L100 0 L100 100 L0 100 z M5,50 a45,45 0 1,0 90,0 a45,45 0 1,0 -90,0 z",
desiredSize: new go.Size(50, 50),
strokeWidth: 0,
fill: 'white'
},
new go.Binding("fill", "color")
)
),
$$(go.TextBlock,
{ textAlign: "center", maxSize: new go.Size(50, NaN) },
new go.Binding("text", "name"))
));
myDiagram.nodeTemplateMap.add("F", // female
$$(go.Node, "Vertical",
{desiredSize: new go.Size(50, 120)},
$$(go.Panel, "Spot",
$$(go.Picture,
{ margin: 10, width: 49, height: 49, background: "white"
},
new go.Binding("source")
),
$$(go.Shape, {
geometryString: "F M0 0 L100 0 L100 100 L0 100 z M5,50 a45,45 0 1,0 90,0 a45,45 0 1,0 -90,0 z",
desiredSize: new go.Size(50, 50),
strokeWidth: 0,
fill: 'white'
}
)
),
$$(go.TextBlock,
{ textAlign: "center", maxSize: new go.Size(50, NaN) },
new go.Binding("text", "name"))
));
myDiagram.nodeTemplateMap.add("TT", // to tien: not visible
$$(go.Node, "Vertical",
{desiredSize: new go.Size(0, 0)}
));
myDiagram.linkTemplate = // for parent-child relationships
$$(go.Link,
{
routing: go.Link.Orthogonal, curviness: 15,
layerName: "Background", selectable: false,
fromSpot: go.Spot.Bottom, toSpot: go.Spot.Top
},
$$(go.Shape, { strokeWidth: 2 })
);
myDiagram.linkTemplateMap.add("Marriage", // for marriage relationships
$$(go.Link,
{ layerName: "marry", selectable: false,
isLayoutPositioned: false //why not effect to position of node connected with this link?
},
$$(go.Shape, { strokeWidth: 2, stroke: "blue" })
));
myDiagram.linkTemplateMap.add("childInLaw", // for sonInLaw, daughterInLaw relationships
$$(go.Link,
{ selectable: false},
$$(go.Shape, { strokeWidth: 0})
)
);
jQuery.getJSON("people3.txt", function(jsondata){
setupDiagram(myDiagram, jsondata, 5);
});
}
// create and initialize the Diagram.model given an array of node data representing people
function setupDiagram(diagram, array, focusId) {
diagram.model =
go.GraphObject.make(go.GraphLinksModel,
{ nodeCategoryProperty: "s",
// create all of the nodes for people
nodeDataArray: array.nodeDataArray,
linkDataArray: array.linkDataArray
});
var node = diagram.findNodeForKey(focusId);
if (node !== null) {
diagram.select(node);
}
}
===============
My data: people3.txt
{ “class”: “go.GraphLinksModel”,
“nodeDataArray”: [
{ “key”: -14,“name”: “Ông nội Nguyễn Minh Nhật”, “s”:“TT”, “color”:“red”, “source”: “”},
{ “key”: -13,“name”: “Ông nội Nguyễn Thị Ngân”, “s”:“TT”, “color”:“red”, “source”: “”},
{ “key”: -12,“name”: “Nguyễn Thị Hoa”, “s”:“F”, “color”:“red”, “source”: “47.jpg”},
{ “key”: -11,“name”: “Nguyễn Văn Hào”, “s”:“M”, “color”:“red”, “source”: “46.jpg”},
{ “key”: -10,“name”: “Nguyễn Thị Cấn”, “s”:“F”, “color”:“red”, “source”: “49.jpg”},
{ “key”: -9,“name”: “Nguyễn Văn Chiểu”, “s”:“M”, “color”:“red”, “source”: “48.jpg”},
{ “key”: -8,“name”: “Trần Văn Trượng”, “s”:“M”, “color”:“red”, “source”: “36.jpg”, “birthDate”:"", “dieDate”:"", “web”: “”},
{ “key”: -7,“name”: “Trần Văn Đạ”, “s”:“M”, “color”:“red”, “source”: “35.jpg”},
{ “key”: -6,“name”: “Trương Thị Tý”, “s”:“F”, “color”:“red”, “source”: “34.jpg”},
{ “key”: -5,“name”: “Trần Thị Lý”, “s”:“F”, “color”:“red”, “source”: “33.jpg”},
{ “key”: -4,“name”: “Trần Thị Lựu”, “s”:“F”, “color”:“red”, “source”: “32.jpg”},
{ “key”: -3,“name”: “Trần Trọng Đạt”, “s”:“M”, “color”:“red”, “source”: “31.jpg”},
{ “key”: -2,“name”: “Trần Quốc Nguyên”, “s”:“M”, “color”:“red”, “source”: “30.jpg”},
{ “key”: -1,“name”: “Trần Văn Mẫn”, “s”:“M”, “color”:“red”, “source”: “29.jpg”},
{ “key”: 0, “name”: “Trần Văn Cầu”, “s”:“M”, “color”:“red”, “source”: “28.jpg”},
{ “key”: 1, “name”: “Nguyễn Thị Ngân”, “s”:“F”, “color”:“red”, “source”: “1.jpg”},
{ “key”: 2, “name”: “Trần Bích Ngọc”, “s”:“F”, “color”:“red”, “source”: “2.jpg”},
{ “key”: 3, “name”: “Trần Thị Ánh Tuyết”, “s”:“F”, “color”:“red”, “source”: “3.jpg”},
{ “key”: 4, “name”: “Trần Ngọc Dung”, “s”:“F”, “color”:“red”, “source”: “4.jpg”},
{ “key”: 5, “name”: “Trần Đại Long”, “s”:“M”, “color”:“red”, “source”: “5.jpg”},
{ “key”: 6, “name”: “Trần Bích Hạnh”, “s”:“F”, “color”:“red”, “source”: “6.png”},
{ “key”: 7, “name”: “Hoàng Minh Đức”, “s”:“M”, “color”:“red”, “source”: “7.jpg”} ,
{ “key”: 8, “name”: “Hoàng Minh Hiếu”, “s”:“M”, “color”:“red”, “source”: “8.jpg”} ,
{ “key”: 9, “name”: “Mai Thị Ánh Hồng”, “s”:“F”, “color”:“red”, “source”: “9.jpg”},
{ “key”: 10, “name”: “Mai Bình An”, “s”:“F”, “color”:“red”, “source”: “10.jpg”},
{ “key”: 11, “name”: “Mai Thị Cát Tường”, “s”:“F”, “color”:“red”, “source”: “11.jpg”},
{ “key”: 12, “name”: “Trần T Thanh Tú”, “s”:“F”, “color”:“red”, “source”: “12.jpg”},
{ “key”: 13, “name”: “Đoàn Xuân Tâm”, “s”:“M”, “color”:“red”, “source”: “13.jpg”},
{ “key”: 14, “name”: “Đoàn Xuân Hòa”, “s”:“M”, “color”:“red”, “source”: “14.jpg”},
{ “key”: 15, “name”: “Hoàng Ngọc Lan Tiên”, “s”:“F”, “color”:“red”, “source”: “15.jpg”},
{ “key”: 17,“name”: “Hoàng Hồng Phúc”, “s”:“M”, “color”:“red”, “source”: “17.jpg”},
{ “key”: 18,“name”: “Mai Sỹ Hùng”, “s”:“M”, “color”:“red”, “source”: “18.jpg”},
{ “key”: 19,“name”: “Nguyễn Minh Nhật”, “s”:“M”, “color”:“red”, “source”: “19.jpg”},
{ “key”: 20,“name”: “Bùi Thị Thanh Minh”, “s”:“F”, “color”:“red”, “source”: “20.jpg”},
{ “key”: 21,“name”: “Đoàn Văn Xuân”, “s”:“M”, “color”:“red”, “source”: “21.jpg”},
{ “key”: 22,“name”: “Phạm Thị Hảo”, “s”:“F”, “color”:“red”, “source”: “22.jpg”},
{ “key”: 23,“name”: “Trần Tuyết Vân”, “s”:“F”, “color”:“red”, “source”: “23.jpg”},
{ “key”: 24,“name”: “Trần Thúy Loan”, “s”:“F”, “color”:“red”, “source”: “24.jpg”},
{ “key”: 25,“name”: “Trần Quốc Tuấn”, “s”:“M”, “color”:“red”, “source”: “25.jpg”},
{ “key”: 26,“name”: “Trần Thúy Ngọc”, “s”:“F”, “color”:“red”, “source”: “26.jpg”},
{ “key”: 27,“name”: “Trần Quốc Khánh”, “s”:“M”, “color”:“red”, “source”: “27.jpg”},
{ “key”: 28,“name”: “Trần Văn Tân”, “s”:“M”, “color”:“red”, “source”: “37.jpg”},
{ “key”: 29,“name”: “Trần Thị Tuyết”, “s”:“F”, “color”:“red”, “source”: “38.jpg”},
{ “key”: 30,“name”: “Trần Hồng Sương”, “s”:“F”, “color”:“red”, “source”: “39.jpg”},
{ “key”: 31,“name”: “Lê Văn Hào”, “s”:“M”, “color”:“red”, “source”: “40.jpg”},
{ “key”: 32,“name”: “Nguyễn Văn Trầm”, “s”:“M”, “color”:“red”, “source”: “41.jpg”},
{ “key”: 33,“name”: “Nguyễn Hoàng Đức”, “s”:“M”, “color”:“red”, “source”: “50.jpg”},
{ “key”: 34,“name”: “Nguyễn Hoàng Trung”, “s”:“M”, “color”:“red”, “source”: “51.jpg”}
],
“linkDataArray”: [
{ “from”: -8, “to”: -7},
{ “from”: -7, “to”: -5},
{ “from”: -7, “to”: -4},
{ “from”: -7, “to”: -3},
{ “from”: -7, “to”: -2},
{ “from”: -7, “to”: -1},
{ “from”: -7, “to”: 0},
{ “from”: -3, “to”: 28},
{ “from”: -3, “to”: 29},
{ “from”: -3, “to”: 30},
{ “from”: -3, “to”: 2},
{ “from”: -3, “to”: 3},
{ “from”: -3, “to”: 4},
{ “from”: -3, “to”: 5},
{ “from”: -3, “to”: 6},
{ “from”: 2, “to”: 7},
{ “from”: 2, “to”: 8},
{ “from”: 3, “to”: 9},
{ “from”: 3, “to”: 10},
{ “from”: 3, “to”: 11},
{ “from”: 5, “to”: 12},
{ “from”: 6, “to”: 13},
{ “from”: 6, “to”: 14},
{ “from”: 7, “to”: 15},
{ “from”: -2, “to”: 24},
{ “from”: -2, “to”: 25},
{ “from”: -2, “to”: 26},
{ “from”: 30, “to”: 33},
{ “from”: 30, “to”: 34},
{ “from”: -2, “to”: 27},
{ “from”: -9, “to”: 1},
{ “from”: -11, “to”: 19},
{ “from”: -9, “to”: -10, “category”:“Marriage”},
{ “from”: 2, “to”: 17, “category”:“Marriage”},
{ “from”: 3, “to”: 18, “category”:“Marriage”},
{ “from”: 4, “to”: 19, “category”:“Marriage”},
{ “from”: 5, “to”: 20, “category”:“Marriage”},
{ “from”: 6, “to”: 21, “category”:“Marriage”},
{ “from”: 7, “to”: 22, “category”:“Marriage”},
{ “from”: -3, “to”: 1, “category”:“Marriage”},
{ “from”: -7, “to”: -6, “category”:“Marriage”},
{ “from”: -2, “to”: 23, “category”:“Marriage”},
{ “from”: 29, “to”: 31, “category”:“Marriage”},
{ “from”: 30, “to”: 32, “category”:“Marriage”},
{ “from”: -11, “to”: -12, “category”:“Marriage”},
{ “from”: -7, “to”: 23, “category”: “childInLaw”},
{ “from”: -8, “to”: -6, “category”: “childInLaw”},
{ “from”: -9, “to”: -3, “category”: “childInLaw”},
{ “from”: -11, “to”: 4, “category”: “childInLaw”},
{ “from”: -3, “to”: 17, “category”: “childInLaw”},
{ “from”: -3, “to”: 18, “category”: “childInLaw”},
{ “from”: -3, “to”: 19, “category”: “childInLaw”},
{ “from”: -3, “to”: 20, “category”: “childInLaw”},
{ “from”: -3, “to”: 21, “category”: “childInLaw”},
{ “from”: -3, “to”: 31, “category”: “childInLaw”},
{ “from”: -3, “to”: 32, “category”: “childInLaw”},
{ “from”: 2, “to”: 22, “category”: “childInLaw”},
{ “from”: -13, “to”: -9, “category”: “childInLaw”},
{ “from”: -13, “to”: -10, “category”: “childInLaw”},
{ “from”: -14, “to”: -11, “category”: “childInLaw”},
{ “from”: -14, “to”: -12, “category”: “childInLaw”}
]
}
I use “childInLaw” link to make spouses the same level, due to same parent. For couple at top level, I create an invisible parent node (TT). So not need custom layout.
This way is even simpler than previous. However, I am not satisfied because marriage couple does not stand together, so there are many crossing links between couple. Please advise me how to arrange nodes in order from left to right?
Thank you very much.