Published
Edited
Oct 9, 2021
1 star
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
graph = {
bDisplayContext;
bDisplayLabeledContext;

let graph = {
nodes: [],
links: []
};

for (let i = 0; i < numberOfNodes; i++) {
graph.nodes.push({
id: i,
name: i + 1 + "",
width: 60,
height: 40,
visible: true,
relativeX: 0,
relativeY: 5
});

for (let j = i + 1; j < numberOfNodes; j++) {
graph.links.push({ source: i, target: j, visible: true });
}
}
svgs.selectAll(".cleanOnInit").remove();
return graph;
}
Insert cell
computeTorusLayout = (
graph,
twoEdgeWrappingGraph,
threeEdgeWrappingGraph,
bEdgeWrappingConfig,
boundaryConfig
) => {
if (
twoEdgeWrappingGraph != null &&
twoEdgeWrappingGraph.links != null &&
twoEdgeWrappingGraph.nodes != null
) {
//restore visibility of edges on graph
//overwrite all the wrapping edges derived in previous layout round
graph.links.forEach(d => {
d.visible = true;
});

graph.links.forEach(d => {
if (d.source.visible && d.target.visible) d.visible = true;
else d.visible = false;
});
twoEdgeWrappingGraph.nodes.splice(0, twoEdgeWrappingGraph.nodes.length);
twoEdgeWrappingGraph.links.splice(0, twoEdgeWrappingGraph.links.length);
threeEdgeWrappingGraph.nodes.splice(0, threeEdgeWrappingGraph.nodes.length);
threeEdgeWrappingGraph.links.splice(0, threeEdgeWrappingGraph.links.length);
wrappingNodeLabel.splice(0, wrappingNodeLabel.length);

for (let node of graph.nodes) {
if (node.visible) {
twoEdgeWrappingGraph.nodes.push({
x: node.x,
y: node.y,
visible: false,
name: ""
});
threeEdgeWrappingGraph.nodes.push({
x: node.x,
y: node.y,
visible: false,
name: ""
});
}
}
}
let sourceX = 0;
let sourceY = 0;
let targetX = 0;
let targetY = 0;
let mappingNodes = null;
let intersectionPoints = null;
let tmpDistance = 0;
let edgeLength;

for (let edge of graph.links) {
if (edge.visible) {
sourceX = edge.source.x;
sourceY = edge.source.y;
targetX = edge.target.x;
targetY = edge.target.y;
edgeLength = { value: 0 };
tmpDistance = 0;

mappingNodes = findMappingNodesFromOtherSquares(
targetX,
targetY,
boundaryConfig
);
intersectionPoints = findIntersectionPointsWithShortestEdgeBtnSourceAndTarget(
sourceX,
sourceY,
targetX,
targetY,
mappingNodes,
edgeLength,
bEdgeWrappingConfig,
boundaryConfig
);
tmpDistance = findEuclideanDistance(sourceX, sourceY, targetX, targetY);

if (
edgeLength.value < tmpDistance &&
intersectionPoints != null &&
intersectionPoints.length > 0
) {
if (intersectionPoints.length > 2 && intersectionPoints.length < 5) {
threeEdgeWrappingGraph.nodes.push({
x: intersectionPoints[0].x,
y: intersectionPoints[0].y,
visible: true,
name: edge.target.name
});
threeEdgeWrappingGraph.nodes.push({
x: intersectionPoints[1].x,
y: intersectionPoints[1].y,
visible: true,
name: edge.source.name
});
threeEdgeWrappingGraph.links.push({
x1: intersectionPoints[0].x,
y1: intersectionPoints[0].y,
x2: sourceX,
y2: sourceY,
visible: true
});
threeEdgeWrappingGraph.links.push({
x1: intersectionPoints[1].x,
y1: intersectionPoints[1].y,
x2: targetX,
y2: targetY,
visible: true
});

threeEdgeWrappingGraph.nodes.push({
x: intersectionPoints[2].x,
y: intersectionPoints[2].y,
visible: true
});
threeEdgeWrappingGraph.nodes.push({
x: intersectionPoints[3].x,
y: intersectionPoints[3].y,
visible: true
});
threeEdgeWrappingGraph.links.push({
x1: intersectionPoints[2].x,
y1: intersectionPoints[2].y,
x2: intersectionPoints[3].x,
y2: intersectionPoints[3].y,
visible: true
});

mutable wrappingNodeLabel.push({
x: intersectionPoints[0].x,
y: intersectionPoints[0].y,
visible: true,
name: edge.target.name
});
mutable wrappingNodeLabel.push({
x: intersectionPoints[1].x,
y: intersectionPoints[1].y,
visible: true,
name: edge.source.name
});
} else {
twoEdgeWrappingGraph.nodes.push({
x: intersectionPoints[0].x,
y: intersectionPoints[0].y,
visible: true,
name: edge.target.name
});
twoEdgeWrappingGraph.nodes.push({
x: intersectionPoints[1].x,
y: intersectionPoints[1].y,
visible: true,
name: edge.source.name
});
twoEdgeWrappingGraph.links.push({
x1: intersectionPoints[0].x,
y1: intersectionPoints[0].y,
x2: sourceX,
y2: sourceY,
visible: true
});
twoEdgeWrappingGraph.links.push({
x1: intersectionPoints[1].x,
y1: intersectionPoints[1].y,
x2: targetX,
y2: targetY,
visible: true
});

mutable wrappingNodeLabel.push({
x: intersectionPoints[0].x,
y: intersectionPoints[0].y,
visible: true,
name: edge.target.name
});
mutable wrappingNodeLabel.push({
x: intersectionPoints[1].x,
y: intersectionPoints[1].y,
visible: true,
name: edge.source.name
});
}
edge.visible = false;
}
}
mutable numberOfTwoEdgeWrappingLinks = 0;
twoEdgeWrappingGraph.links.forEach(d => {
mutable numberOfTwoEdgeWrappingLinks++;
});

mutable numberOfThreeEdgeWrappingLinks = 0;
threeEdgeWrappingGraph.links.forEach(d => {
mutable numberOfThreeEdgeWrappingLinks++;
});
}

mutable numberOfNonWrappingLinks = 0;
graph.links.forEach(d => {
if (d.visible) mutable numberOfNonWrappingLinks++;
});
}
Insert cell
findMappingNodesFromOtherSquares = (x, y, boundaryConfig) => {
//initialise mapping array
let mappingNodes = new Array();

if (
x < boundaryConfig.oneThirdWidth &&
x > boundaryConfig.twoThirdsWidth &&
y < boundaryConfig.oneThirdHeight &&
y > boundaryConfig.twoThirdsHeight
)
mappingNodes = null;

if (mappingNodes != null) {
//calculate x, y position of nodes in the 4 adjacent squares
//in the order of [left, up, right, bottom]
mappingNodes.push({
x: x - boundaryConfig.oneThirdWidth,
y: y,
intersectedX: boundaryConfig.oneThirdWidth,
intersectedY: 0
});
mappingNodes.push({
x: x,
y: y - boundaryConfig.oneThirdHeight,
intersectedX: 0,
intersectedY: boundaryConfig.oneThirdHeight
});
mappingNodes.push({
x: x + boundaryConfig.oneThirdWidth,
y: y,
intersectedX: boundaryConfig.twoThirdsWidth,
intersectedY: 0
});
mappingNodes.push({
x: x,
y: y + boundaryConfig.oneThirdHeight,
intersectedX: 0,
intersectedY: boundaryConfig.twoThirdsHeight
});

//calculate x, y position of nodes in the 4 corner squares
//in the order of [upper-left, upper-right, bottom-right bottom-left]
mappingNodes.push({
x: x - boundaryConfig.oneThirdWidth,
y: y - boundaryConfig.oneThirdHeight,
intersectedX: boundaryConfig.oneThirdWidth,
intersectedY: boundaryConfig.oneThirdHeight
});
mappingNodes.push({
x: x + boundaryConfig.oneThirdWidth,
y: y - boundaryConfig.oneThirdHeight,
intersectedX: boundaryConfig.twoThirdsWidth,
intersectedY: boundaryConfig.oneThirdHeight
});
mappingNodes.push({
x: x + boundaryConfig.oneThirdWidth,
y: y + boundaryConfig.oneThirdHeight,
intersectedX: boundaryConfig.twoThirdsWidth,
intersectedY: boundaryConfig.twoThirdsHeight
});
mappingNodes.push({
x: x - boundaryConfig.oneThirdWidth,
y: y + boundaryConfig.oneThirdHeight,
intersectedX: boundaryConfig.oneThirdWidth,
intersectedY: boundaryConfig.twoThirdsHeight
});
}
return mappingNodes;
}
Insert cell
findIntersectionPointsWithShortestEdgeBtnSourceAndTarget = (
sourceX,
sourceY,
targetX,
targetY,
mappingNodes,
edgeLength,
bEdgeWrappingConfig,
boundaryConfig
) => {
let intersectionPoints = new Array();
let shortestEuclideanDistance = Number.MAX_VALUE;

//ignore if mappingNodes is null or empty
if (mappingNodes == null) intersectionPoints = null;
else {
for (let mappingNode of mappingNodes) {
let intersectedX1 = 0;
let intersectedY1 = 0;
let intersectedX2 = 0;
let intersectedY2 = 0;
let intersectedX3 = 0;
let intersectedY3 = 0;
let intersectedX4 = 0;
let intersectedY4 = 0;

//if intersection resides in a corner square, check which of the two sides of the the boundary it intersects
if (
mappingNode.intersectedX != 0 &&
mappingNode.intersectedY != 0 &&
bEdgeWrappingConfig[1]
) {
//upper-left corner
if (
mappingNode.intersectedX == boundaryConfig.oneThirdWidth &&
mappingNode.intersectedY == boundaryConfig.oneThirdHeight
) {
let testX1 = mappingNode.intersectedX;
let testY1 = getYGivenXAndSrcMappingNodes(
sourceX,
sourceY,
mappingNode.x,
mappingNode.y,
mappingNode.intersectedX
);

let testY2 = mappingNode.intersectedY;
let testX2 = getXGivenYAndSrcMappingNodes(
sourceX,
sourceY,
mappingNode.x,
mappingNode.y,
mappingNode.intersectedY
);

//check whether testY1 is within center square boundary
if (
testY1 >= boundaryConfig.oneThirdHeight &&
testY1 <= boundaryConfig.twoThirdsHeight
) {
intersectedX1 = mappingNode.intersectedX;
intersectedY1 = testY1;

intersectedX2 = testX2 + boundaryConfig.oneThirdWidth;
intersectedY2 = testY2 + boundaryConfig.oneThirdHeight;

intersectedX3 = intersectedX1 + boundaryConfig.oneThirdWidth;
intersectedY3 = intersectedY1;

intersectedX4 = testX2 + boundaryConfig.oneThirdWidth;
intersectedY4 = testY2;
} else if (
testX2 >= boundaryConfig.oneThirdWidth &&
testX2 <= boundaryConfig.twoThirdsWidth
) {
intersectedX1 = testX2;
intersectedY1 = mappingNode.intersectedY;

intersectedX2 = testX1 + boundaryConfig.oneThirdWidth;
intersectedY2 = testY1 + boundaryConfig.oneThirdHeight;

intersectedX3 = intersectedX1;
intersectedY3 = intersectedY1 + boundaryConfig.oneThirdHeight;

intersectedX4 = testX1;
intersectedY4 = testY1 + boundaryConfig.oneThirdHeight;
}
}
//upper-right corner
else if (
mappingNode.intersectedX == boundaryConfig.twoThirdsWidth &&
mappingNode.intersectedY == boundaryConfig.oneThirdHeight
) {
let testX1 = mappingNode.intersectedX;
let testY1 = getYGivenXAndSrcMappingNodes(
sourceX,
sourceY,
mappingNode.x,
mappingNode.y,
mappingNode.intersectedX
);

let testY2 = mappingNode.intersectedY;
let testX2 = getXGivenYAndSrcMappingNodes(
sourceX,
sourceY,
mappingNode.x,
mappingNode.y,
mappingNode.intersectedY
);

//check whether testY1 is within center square boundary
if (
testY1 >= boundaryConfig.oneThirdHeight &&
testY1 <= boundaryConfig.twoThirdsHeight
) {
intersectedX1 = mappingNode.intersectedX;
intersectedY1 = testY1;

intersectedX2 = testX2 - boundaryConfig.oneThirdWidth;
intersectedY2 = testY2 + boundaryConfig.oneThirdHeight;

intersectedX3 = intersectedX1 - boundaryConfig.oneThirdWidth;
intersectedY3 = intersectedY1;

intersectedX4 = testX2 - boundaryConfig.oneThirdWidth;
intersectedY4 = testY2;
} else if (
testX2 >= boundaryConfig.oneThirdWidth &&
testX2 <= boundaryConfig.twoThirdsWidth
) {
intersectedX1 = testX2;
intersectedY1 = mappingNode.intersectedY;

intersectedX2 = testX1 - boundaryConfig.oneThirdWidth;
intersectedY2 = testY1 + boundaryConfig.oneThirdHeight;

intersectedX3 = intersectedX1;
intersectedY3 = intersectedY1 + boundaryConfig.oneThirdHeight;

intersectedX4 = testX1;
intersectedY4 = testY1 + boundaryConfig.oneThirdHeight;
}
}
//bottom-right corner
else if (
mappingNode.intersectedX == boundaryConfig.twoThirdsWidth &&
mappingNode.intersectedY == boundaryConfig.twoThirdsHeight
) {
let testX1 = mappingNode.intersectedX;
let testY1 = getYGivenXAndSrcMappingNodes(
sourceX,
sourceY,
mappingNode.x,
mappingNode.y,
mappingNode.intersectedX
);

let testY2 = mappingNode.intersectedY;
let testX2 = getXGivenYAndSrcMappingNodes(
sourceX,
sourceY,
mappingNode.x,
mappingNode.y,
mappingNode.intersectedY
);

//check whether testY1 is within center square boundary
if (
testY1 >= boundaryConfig.oneThirdHeight &&
testY1 <= boundaryConfig.twoThirdsHeight
) {
intersectedX1 = mappingNode.intersectedX;
intersectedY1 = testY1;

intersectedX2 = testX2 - boundaryConfig.oneThirdWidth;
intersectedY2 = testY2 - boundaryConfig.oneThirdHeight;

intersectedX3 = intersectedX1 - boundaryConfig.oneThirdWidth;
intersectedY3 = intersectedY1;

intersectedX4 = testX2 - boundaryConfig.oneThirdWidth;
intersectedY4 = testY2;
} else if (
testX2 >= boundaryConfig.oneThirdWidth &&
testX2 <= boundaryConfig.twoThirdsWidth
) {
intersectedX1 = testX2;
intersectedY1 = mappingNode.intersectedY;

intersectedX2 = testX1 - boundaryConfig.oneThirdWidth;
intersectedY2 = testY1 - boundaryConfig.oneThirdHeight;

intersectedX3 = intersectedX1;
intersectedY3 = intersectedY1 - boundaryConfig.oneThirdHeight;

intersectedX4 = testX1;
intersectedY4 = testY1 - boundaryConfig.oneThirdHeight;
}
}
//bottom-left corner
else if (
mappingNode.intersectedX == boundaryConfig.oneThirdWidth &&
mappingNode.intersectedY == boundaryConfig.twoThirdsHeight
) {
let testX1 = mappingNode.intersectedX;
let testY1 = getYGivenXAndSrcMappingNodes(
sourceX,
sourceY,
mappingNode.x,
mappingNode.y,
mappingNode.intersectedX
);

let testY2 = mappingNode.intersectedY;
let testX2 = getXGivenYAndSrcMappingNodes(
sourceX,
sourceY,
mappingNode.x,
mappingNode.y,
mappingNode.intersectedY
);

//check whether testY1 is within center square boundary
if (
testY1 >= boundaryConfig.oneThirdHeight &&
testY1 <= boundaryConfig.twoThirdsHeight
) {
intersectedX1 = mappingNode.intersectedX;
intersectedY1 = testY1;

intersectedX2 = testX2 + boundaryConfig.oneThirdWidth;
intersectedY2 = testY2 - boundaryConfig.oneThirdHeight;

intersectedX3 = intersectedX1 + boundaryConfig.oneThirdWidth;
intersectedY3 = intersectedY1;

intersectedX4 = testX2 + boundaryConfig.oneThirdWidth;
intersectedY4 = testY2;
} else if (
testX2 >= boundaryConfig.oneThirdWidth &&
testX2 <= boundaryConfig.twoThirdsWidth
) {
intersectedX1 = testX2;
intersectedY1 = mappingNode.intersectedY;

intersectedX2 = testX1 + boundaryConfig.oneThirdWidth;
intersectedY2 = testY1 - boundaryConfig.oneThirdHeight;

intersectedX3 = intersectedX1;
intersectedY3 = intersectedY1 - boundaryConfig.oneThirdHeight;

intersectedX4 = testX1;
intersectedY4 = testY1 - boundaryConfig.oneThirdHeight;
}
}
} else if (mappingNode.intersectedX != 0 && bEdgeWrappingConfig[0]) {
intersectedX1 = mappingNode.intersectedX;
intersectedY1 = getYGivenXAndSrcMappingNodes(
sourceX,
sourceY,
mappingNode.x,
mappingNode.y,
mappingNode.intersectedX
);
intersectedX2 =
mappingNode.intersectedX == boundaryConfig.oneThirdWidth
? boundaryConfig.twoThirdsWidth
: boundaryConfig.oneThirdWidth;
intersectedY2 = intersectedY1;
} else if (mappingNode.intersectedY != 0 && bEdgeWrappingConfig[0]) {
intersectedY1 = mappingNode.intersectedY;
intersectedX1 = getXGivenYAndSrcMappingNodes(
sourceX,
sourceY,
mappingNode.x,
mappingNode.y,
mappingNode.intersectedY
);

intersectedX2 = intersectedX1;
intersectedY2 =
mappingNode.intersectedY == boundaryConfig.oneThirdHeight
? boundaryConfig.twoThirdsHeight
: boundaryConfig.oneThirdHeight;
}
let tmpDistanceBtnSourceAndIntersectPt1 = findEuclideanDistance(
intersectedX1,
intersectedY1,
sourceX,
sourceY
);
let tmpDistanceBtnTargetAndIntersectPt2 = findEuclideanDistance(
intersectedX2,
intersectedY2,
targetX,
targetY
);

let sumOfTwoEdgeSegmentLength =
tmpDistanceBtnSourceAndIntersectPt1 +
tmpDistanceBtnTargetAndIntersectPt2;

//compute sum of two edge segment for wrapping on adjacent squares
if (
bEdgeWrappingConfig[0] &&
(mappingNode.intersectedX == 0 || mappingNode.intersectedY == 0) &&
shortestEuclideanDistance > sumOfTwoEdgeSegmentLength
) {
if (mappingNode.intersectedX != 0 || mappingNode.intersectedY != 0) {
shortestEuclideanDistance = sumOfTwoEdgeSegmentLength;
edgeLength.value = shortestEuclideanDistance;
intersectionPoints.splice(0, intersectionPoints.length);
intersectionPoints.push({ x: intersectedX1, y: intersectedY1 });
intersectionPoints.push({ x: intersectedX2, y: intersectedY2 });
}
}
//compute sum of three edge segment for wrapping on corner squares
else if (
bEdgeWrappingConfig[1] &&
(mappingNode.intersectedX != 0 && mappingNode.intersectedY != 0) &&
shortestEuclideanDistance > sumOfTwoEdgeSegmentLength
) {
let tmpDistanceBtnIntersectPt3Pt4 = findEuclideanDistance(
intersectedX3,
intersectedY3,
intersectedX4,
intersectedY4
);
if (
shortestEuclideanDistance >
tmpDistanceBtnIntersectPt3Pt4 + sumOfTwoEdgeSegmentLength
) {
shortestEuclideanDistance =
tmpDistanceBtnIntersectPt3Pt4 + sumOfTwoEdgeSegmentLength;
edgeLength.value = shortestEuclideanDistance;
intersectionPoints.splice(0, intersectionPoints.length);
intersectionPoints.push({ x: intersectedX1, y: intersectedY1 });
intersectionPoints.push({ x: intersectedX2, y: intersectedY2 });
intersectionPoints.push({ x: intersectedX3, y: intersectedY3 });
intersectionPoints.push({ x: intersectedX4, y: intersectedY4 });
}
}
}
}
return intersectionPoints;
}
Insert cell
getYGivenXAndSrcMappingNodes = (x1, y1, x2, y2, x) => {
let y = 0;
if(x1 == x2)
y = (y1+y2)/2;
else {
y = (x2*y1-x*y1-x1*y2+x*y2)/(x2-x1);
}
return y;
}
Insert cell
getXGivenYAndSrcMappingNodes = (x1, y1, x2, y2, y) => {
let x = 0;
if(y1 == y2)
x = (x1+x2)/2;
else {
x = (x1*y2-x2*y1-x1*y+x2*y)/(y2-y1);
}
return x;
}
Insert cell
findEuclideanDistance = (x1, y1, x2, y2) => {
return Math.sqrt(Math.pow(x1-x2,2) + Math.pow(y1-y2,2));
}
Insert cell
boundaryConfig = ({
"oneThirdWidth": configuration.svgWidth/3,
"twoThirdsWidth": configuration.svgWidth*2/3,
"oneThirdHeight": configuration.svgHeight/3,
"twoThirdsHeight": configuration.svgHeight*2/3
});
Insert cell
boundary = {
let boundary = new Array();
boundary.push({"x1":boundaryConfig.oneThirdWidth, "y1":0, "x2":boundaryConfig.oneThirdWidth, "y2":configuration.svgHeight, "strokeWidth":1});
boundary.push({"x1":boundaryConfig.twoThirdsWidth, "y1":0, "x2":boundaryConfig.twoThirdsWidth, "y2":configuration.svgHeight, "strokeWidth":1});
boundary.push({"x1":0, "y1":boundaryConfig.oneThirdHeight, "x2":configuration.svgWidth, "y2":boundaryConfig.oneThirdHeight, "strokeWidth":1});
boundary.push({"x1":0, "y1":boundaryConfig.twoThirdsHeight, "x2":configuration.svgWidth, "y2":boundaryConfig.twoThirdsHeight, "strokeWidth":1});
boundary.push({"x1":boundaryConfig.oneThirdWidth, "y1":boundaryConfig.oneThirdHeight, "x2":boundaryConfig.twoThirdsWidth, "y2":boundaryConfig.oneThirdHeight, "strokeWidth":5});
boundary.push({"x1":boundaryConfig.oneThirdWidth, "y1":boundaryConfig.twoThirdsHeight, "x2":boundaryConfig.twoThirdsWidth, "y2":boundaryConfig.twoThirdsHeight, "strokeWidth":5});
return boundary;
}
Insert cell
verticalBoundary = {
let verticalBoundary = new Array();
verticalBoundary.push({"x1":boundaryConfig.oneThirdWidth, "y1":boundaryConfig.oneThirdHeight, "x2":boundaryConfig.oneThirdWidth, "y2":boundaryConfig.twoThirdsHeight, "strokeWidth":5});
verticalBoundary.push({"x1":boundaryConfig.twoThirdsWidth, "y1":boundaryConfig.oneThirdHeight, "x2":boundaryConfig.twoThirdsWidth, "y2":boundaryConfig.twoThirdsHeight, "strokeWidth":5});
return verticalBoundary;
}
Insert cell
draggableVerticalBoundary = {
let draggableVerticalBoundary = svgs
.selectAll(".verticalBourndary")
.data(verticalBoundary)
.enter()
.append("line")
.attr("x1", d => {
return d.x1;
})
.attr("y1", d => {
return d.y1;
})
.attr("x2", d => {
return d.x2;
})
.attr("y2", d => {
return d.y2;
})
.attr("class", "bourndary" + " cleanOnInit")
.style("stroke", "#BED7F6")
.style("stroke-width", d => {
return d.strokeWidth;
})
.call(
d3
.drag()
.on('start', () => {
draggableBorderConfig.dragStartX = d3.event.x;
draggableBorderConfig.dragStartY = d3.event.y;
/** Preventing propagation of dragstart to parent elements */
d3.event.sourceEvent.stopPropagation();
})
.on('drag', () => {
configuration.svgWidth +=
d3.event.x - draggableBorderConfig.dragStartX;
configuration.svgHeight = configuration.svgWidth;
draggableBorderConfig.dragStartX = d3.event.x;
draggableBorderConfig.dragStartY = d3.event.x;
border[0] = {
x1: 0,
y1: 0,
x2: configuration.svgWidth,
y2: 0,
strokeWidth: 1
};
border[1] = {
x1: 0,
y1: 0,
x2: 0,
y2: configuration.svgHeight,
strokeWidth: 1
};
horizontalBorder[0] = {
x1: 0,
y1: configuration.svgHeight,
x2: configuration.svgWidth,
y2: configuration.svgHeight,
strokeWidth: 1
};
verticalBorder[0] = {
x1: configuration.svgWidth,
y1: 0,
x2: configuration.svgWidth,
y2: configuration.svgHeight,
strokeWidth: 1
};
boundaryConfig.oneThirdWidth = configuration.svgWidth / 3;
boundaryConfig.twoThirdsWidth = (configuration.svgWidth * 2) / 3;
boundaryConfig.oneThirdHeight = configuration.svgHeight / 3;
boundaryConfig.twoThirdsHeight = (configuration.svgHeight * 2) / 3;

mutable centerSquareWidth = configuration.svgWidth;
mutable centerSquareHeight = configuration.svgHeight;
boundary[0] = {
x1: boundaryConfig.oneThirdWidth,
y1: 0,
x2: boundaryConfig.oneThirdWidth,
y2: configuration.svgHeight,
strokeWidth: 1
};
boundary[1] = {
x1: boundaryConfig.twoThirdsWidth,
y1: 0,
x2: boundaryConfig.twoThirdsWidth,
y2: configuration.svgHeight,
strokeWidth: 1
};
boundary[2] = {
x1: 0,
y1: boundaryConfig.oneThirdHeight,
x2: configuration.svgWidth,
y2: boundaryConfig.oneThirdHeight,
strokeWidth: 1
};
boundary[3] = {
x1: 0,
y1: boundaryConfig.twoThirdsHeight,
x2: configuration.svgWidth,
y2: boundaryConfig.twoThirdsHeight,
strokeWidth: 1
};
boundary[4] = {
x1: boundaryConfig.oneThirdWidth,
y1: boundaryConfig.oneThirdHeight,
x2: boundaryConfig.twoThirdsWidth,
y2: boundaryConfig.oneThirdHeight,
strokeWidth: 5
};
boundary[5] = {
x1: boundaryConfig.oneThirdWidth,
y1: boundaryConfig.twoThirdsHeight,
x2: boundaryConfig.twoThirdsWidth,
y2: boundaryConfig.twoThirdsHeight,
strokeWidth: 5
};
verticalBoundary[0] = {
x1: boundaryConfig.oneThirdWidth,
y1: boundaryConfig.oneThirdHeight,
x2: boundaryConfig.oneThirdWidth,
y2: boundaryConfig.twoThirdsHeight,
strokeWidth: 5
};
verticalBoundary[1] = {
x1: boundaryConfig.twoThirdsWidth,
y1: boundaryConfig.oneThirdHeight,
x2: boundaryConfig.twoThirdsWidth,
y2: boundaryConfig.twoThirdsHeight,
strokeWidth: 5
};

borderVisual
.data(border)
.attr("x1", d => {
return d.x1;
})
.attr("y1", d => {
return d.y1;
})
.attr("x2", d => {
return d.x2;
})
.attr("y2", d => {
return d.y2;
});
draggableHorizontalBorder
.data(horizontalBorder)
.attr("x1", d => {
return d.x1;
})
.attr("y1", d => {
return d.y1;
})
.attr("x2", d => {
return d.x2;
})
.attr("y2", d => {
return d.y2;
});
draggableVerticalBorder
.data(verticalBorder)
.attr("x1", d => {
return d.x1;
})
.attr("y1", d => {
return d.y1;
})
.attr("x2", d => {
return d.x2;
})
.attr("y2", d => {
return d.y2;
});
boundaryVisual
.data(boundary)
.lower()
.attr("x1", d => {
return d.x1;
})
.attr("y1", d => {
return d.y1;
})
.attr("x2", d => {
return d.x2;
})
.attr("y2", d => {
return d.y2;
});

draggableVerticalBoundary
.data(verticalBoundary)
.attr("x1", d => {
return d.x1;
})
.attr("y1", d => {
return d.y1;
})
.attr("x2", d => {
return d.x2;
})
.attr("y2", d => {
return d.y2;
});
if (d3cola != null && bEnableWrapping)
d3cola.updateConfiguration([
configuration.svgWidth,
configuration.svgHeight
]);
updateTorusLayout(
true,
graph,
twoEdgeWrappingGraph,
threeEdgeWrappingGraph,
boundaryConfig,
bEdgeWrappingConfig
);
})
.on('end', () => {
configuration.svgWidth +=
d3.event.x - draggableBorderConfig.dragStartX;
configuration.svgHeight = configuration.svgWidth;
draggableBorderConfig.dragStartX = d3.event.x;
draggableBorderConfig.dragStartY = d3.event.x;
border[0] = {
x1: 0,
y1: 0,
x2: configuration.svgWidth,
y2: 0,
strokeWidth: 1
};
border[1] = {
x1: 0,
y1: 0,
x2: 0,
y2: configuration.svgHeight,
strokeWidth: 1
};
horizontalBorder[0] = {
x1: 0,
y1: configuration.svgHeight,
x2: configuration.svgWidth,
y2: configuration.svgHeight,
strokeWidth: 1
};
verticalBorder[0] = {
x1: configuration.svgWidth,
y1: 0,
x2: configuration.svgWidth,
y2: configuration.svgHeight,
strokeWidth: 1
};
boundaryConfig.oneThirdWidth = configuration.svgWidth / 3;
boundaryConfig.twoThirdsWidth = (configuration.svgWidth * 2) / 3;
boundaryConfig.oneThirdHeight = configuration.svgHeight / 3;
boundaryConfig.twoThirdsHeight = (configuration.svgHeight * 2) / 3;

mutable centerSquareWidth = configuration.svgWidth;
mutable centerSquareHeight = configuration.svgHeight;
boundary[0] = {
x1: boundaryConfig.oneThirdWidth,
y1: 0,
x2: boundaryConfig.oneThirdWidth,
y2: configuration.svgHeight,
strokeWidth: 1
};
boundary[1] = {
x1: boundaryConfig.twoThirdsWidth,
y1: 0,
x2: boundaryConfig.twoThirdsWidth,
y2: configuration.svgHeight,
strokeWidth: 1
};
boundary[2] = {
x1: 0,
y1: boundaryConfig.oneThirdHeight,
x2: configuration.svgWidth,
y2: boundaryConfig.oneThirdHeight,
strokeWidth: 1
};
boundary[3] = {
x1: 0,
y1: boundaryConfig.twoThirdsHeight,
x2: configuration.svgWidth,
y2: boundaryConfig.twoThirdsHeight,
strokeWidth: 1
};
boundary[4] = {
x1: boundaryConfig.oneThirdWidth,
y1: boundaryConfig.oneThirdHeight,
x2: boundaryConfig.twoThirdsWidth,
y2: boundaryConfig.oneThirdHeight,
strokeWidth: 5
};
boundary[5] = {
x1: boundaryConfig.oneThirdWidth,
y1: boundaryConfig.twoThirdsHeight,
x2: boundaryConfig.twoThirdsWidth,
y2: boundaryConfig.twoThirdsHeight,
strokeWidth: 5
};
verticalBoundary[0] = {
x1: boundaryConfig.oneThirdWidth,
y1: boundaryConfig.oneThirdHeight,
x2: boundaryConfig.oneThirdWidth,
y2: boundaryConfig.twoThirdsHeight,
strokeWidth: 5
};
verticalBoundary[1] = {
x1: boundaryConfig.twoThirdsWidth,
y1: boundaryConfig.oneThirdHeight,
x2: boundaryConfig.twoThirdsWidth,
y2: boundaryConfig.twoThirdsHeight,
strokeWidth: 5
};

borderVisual
.data(border)
.attr("x1", d => {
return d.x1;
})
.attr("y1", d => {
return d.y1;
})
.attr("x2", d => {
return d.x2;
})
.attr("y2", d => {
return d.y2;
});
draggableHorizontalBorder
.data(horizontalBorder)
.attr("x1", d => {
return d.x1;
})
.attr("y1", d => {
return d.y1;
})
.attr("x2", d => {
return d.x2;
})
.attr("y2", d => {
return d.y2;
});
draggableVerticalBorder
.data(verticalBorder)
.attr("x1", d => {
return d.x1;
})
.attr("y1", d => {
return d.y1;
})
.attr("x2", d => {
return d.x2;
})
.attr("y2", d => {
return d.y2;
});
boundaryVisual
.data(boundary)
.lower()
.attr("x1", d => {
return d.x1;
})
.attr("y1", d => {
return d.y1;
})
.attr("x2", d => {
return d.x2;
})
.attr("y2", d => {
return d.y2;
});

draggableVerticalBoundary
.data(verticalBoundary)
.attr("x1", d => {
return d.x1;
})
.attr("y1", d => {
return d.y1;
})
.attr("x2", d => {
return d.x2;
})
.attr("y2", d => {
return d.y2;
});
if (d3cola != null && bEnableWrapping)
d3cola.updateConfiguration([
configuration.svgWidth,
configuration.svgHeight
]);
updateTorusLayout(
true,
graph,
twoEdgeWrappingGraph,
threeEdgeWrappingGraph,
boundaryConfig,
bEdgeWrappingConfig
);
})
);
return draggableVerticalBoundary;
}
Insert cell
boundaryVisual = {
let boundaryVisual = svgs
.selectAll(".bourndary")
.data(boundary)
.enter()
.append("line")
.attr("x1", d => {
return d.x1;
})
.attr("y1", d => {
return d.y1;
})
.attr("x2", d => {
return d.x2;
})
.attr("y2", d => {
return d.y2;
})
.lower()
.attr("class", "bourndary")
.style("stroke", "#BED7F6")
.style("stroke-width", d => {
return d.strokeWidth;
});
return boundaryVisual;
}
Insert cell
border = {
let border = new Array();
border.push({"x1":0, "y1":0, "x2":configuration.svgWidth, "y2":0, "strokeWidth":1});
border.push({"x1":0, "y1":0, "x2":0, "y2":configuration.svgHeight, "strokeWidth":1});
return border;
}
Insert cell
borderVisual = {
let borderVisual = svgs
.selectAll(".border")
.data(border)
.enter()
.append("line")
.attr("x1", d => {
return d.x1;
})
.attr("y1", d => {
return d.y1;
})
.attr("x2", d => {
return d.x2;
})
.attr("y2", d => {
return d.y2;
})
.attr("class", "border")
.style("stroke", "#BED7F6")
.style("stroke-width", d => {
return d.strokeWidth;
});
return borderVisual;
}
Insert cell
draggableHorizontalBorder = {
let draggableHorizontalBorder = svgs
.selectAll(".horizontal_border")
.data(horizontalBorder)
.enter()
.append("line")
.attr("x1", d => {
return d.x1;
})
.attr("y1", d => {
return d.y1;
})
.attr("x2", d => {
return d.x2;
})
.attr("y2", d => {
return d.y2;
})
.attr("class", "horizontal_border")
.style("stroke", "#BED7F6")
.style("stroke-width", d => {
return d.strokeWidth;
});

return draggableHorizontalBorder;
}
Insert cell
horizontalBorder = {
let horizontalBorder = new Array();
horizontalBorder.push({"x1":0, "y1":configuration.svgHeight, "x2":configuration.svgWidth, "y2":configuration.svgHeight, "strokeWidth":1});
return horizontalBorder;
}
Insert cell
draggableVerticalBorder = {
let draggableVerticalBorder = svgs
.selectAll(".vertical_border")
.data(verticalBorder)
.enter()
.append("line")
.attr("x1", d => {
return d.x1;
})
.attr("y1", d => {
return d.y1;
})
.attr("x2", d => {
return d.x2;
})
.attr("y2", d => {
return d.y2;
})
.attr("class", "vertical_border")
.style("stroke", "#BED7F6")
.style("stroke-width", d => {
return d.strokeWidth;
});

return draggableVerticalBorder;
}
Insert cell
verticalBorder = {
let verticalBorder = new Array();
verticalBorder.push({"x1":configuration.svgWidth, "y1":0, "x2":configuration.svgWidth, "y2":configuration.svgHeight, "strokeWidth":1});
return verticalBorder;
}
Insert cell
draggableBorderConfig = ({
"x": -15000,
"y": -15000,
"dragStartX":0,
"dragStartY": 0
})
Insert cell
twoEdgeWrappingGraph = ({
"nodes":[
],
"links":[
]
});
Insert cell
threeEdgeWrappingGraph = ({
"nodes":[
],
"links":[
]
})
Insert cell
svgs = d3.select(visual)
Insert cell
color = d3.scaleOrdinal(d3.schemeCategory10);
Insert cell
transformGroup = (graph, offsetX, offsetY) => {
for (let node of graph.nodes){
node.x += offsetX;
node.y += offsetY;
}
}
Insert cell
svgGroup = {
let svgGroup = svgs
.selectAll(".draggableGroup")
.data([{ x: 0, y: 0 }])
.enter()
.append("g")
.attr("class", "draggableGroup" + " cleanOnInit")
.attr("id", "parentGroup")
.attr("draggable", true)
.lower()
.on('touchstart', () => {
configuration.dragStartX = d3.event.touches[0].clientX;
configuration.dragStartY = d3.event.touches[0].clientY;
})
.on("touchmove", () => {
//apply transforma to graph
transformGroup(
graph,
d3.event.touches[0].clientX - configuration.dragStartX,
d3.event.touches[0].clientY - configuration.dragStartY
);

configuration.dragStartX = d3.event.touches[0].clientX;
configuration.dragStartY = d3.event.touches[0].clientY;

updateTorusLayout(
true,
graph,
twoEdgeWrappingGraph,
threeEdgeWrappingGraph,
boundaryConfig,
bEdgeWrappingConfig
);
})
// .on("touchend", () => {
// //apply transforma to graph
// transformGroup(
// graph,
// d3.event.touches[0].clientX - configuration.dragStartX,
// d3.event.touches[0].clientY - configuration.dragStartY
// );

// configuration.dragStartX = d3.event.touches[0].clientX;
// configuration.dragStartY = d3.event.touches[0].clientY;

// updateTorusLayout(
// true,
// graph,
// twoEdgeWrappingGraph,
// threeEdgeWrappingGraph,
// boundaryConfig,
// bEdgeWrappingConfig
// );
// })
.call(
d3
.drag()
.on('start', () => {
configuration.dragStartX = d3.event.x;
configuration.dragStartY = d3.event.y;
})
.on("drag", () => {
//apply transforma to graph
transformGroup(
graph,
d3.event.x - configuration.dragStartX,
d3.event.y - configuration.dragStartY
);

configuration.dragStartX = d3.event.x;
configuration.dragStartY = d3.event.y;

updateTorusLayout(
true,
graph,
twoEdgeWrappingGraph,
threeEdgeWrappingGraph,
boundaryConfig,
bEdgeWrappingConfig
);
})
.on("end", () => {
//apply transforma to graph
transformGroup(
graph,
d3.event.x - configuration.dragStartX,
d3.event.y - configuration.dragStartY
);

configuration.dragStartX = d3.event.x;
configuration.dragStartY = d3.event.y;

updateTorusLayout(
true,
graph,
twoEdgeWrappingGraph,
threeEdgeWrappingGraph,
boundaryConfig,
bEdgeWrappingConfig
);
})
);
return svgGroup;
}
Insert cell
rectangle = {
let rectangle = svgGroup
.selectAll(".background")
.data([{"x": 0, "y": 0}])
.enter()
.append("rect")
.attr("x", configuration.originX)
.attr("y", configuration.originY)
.attr("rx", 0)
.attr("ry", 0)
.attr("fill-opacity", 0)
.attr("width", configuration.width)
.attr("height", configuration.height)
.attr("class", "background" + " cleanOnInit")
.style("stroke", "black")
.style("fill", "white")
.lower();
return rectangle;
}
Insert cell
configuration = new Object({
originX: 0,
originY: 0,
dragStartX: 0,
dragStartY: 0,
width: 30000,
height: 30000,
svgWidth: 975,
svgHeight: 975
})
Insert cell
d3cola = cola
.d3adaptor(d3)
.linkDistance(120)
.avoidOverlaps(true)
.size([configuration.svgWidth, configuration.svgHeight])
.nodes(graph.nodes)
.links(graph.links)
.enableTorusWrapping(bEnableWrapping)
.on("tick", () => {
updateTorusLayout(
true,
graph,
twoEdgeWrappingGraph,
threeEdgeWrappingGraph,
boundaryConfig,
bEdgeWrappingConfig
);
})
.start()
Insert cell
initRender = {
for (let i in AreaClassCode) {
let areaClassName = AreaClassCode[i];

if (!bEnableWrapping || !bDisplayContext)
areaClassName = AreaClassCode._center.toString(); //draw only center square

let link = svgs
.selectAll(".link" + areaClassName)
.data(graph.links)
.enter()
.append("line")
.attr("class", "link" + areaClassName + " cleanOnInit")
.style("visibility", function(d) {
return "visible";
});

let nodeGroup = svgs
.selectAll(".draggableNodeGroup" + areaClassName)
.data(graph.nodes)
.enter()
.append("g")
.attr("class", "draggableNodeGroup" + areaClassName + " cleanOnInit")
.style("visibility", function(d) {
return d.visible == true ? "visible" : "hidden";
})
.attr("draggable", true)
.call(d3cola.drag);

let group = 7;
let node = nodeGroup
.append("circle")
.attr("class", "node" + areaClassName + " cleanOnInit")
.attr("r", 20)
.attr("cx", 0)
.attr("cy", 0)
.attr("stroke-width", "4px")
.attr("stroke", "black")
.style("fill", function(d) {
return color(d.id);
});

nodeGroup
.append("text")
.attr("class", "label" + areaClassName + " cleanOnInit")
.text(function(d) {
return d.name;
})
.attr("x", function(d) {
return d.relativeX;
})
.attr("y", function(d) {
var that = this;
return d.relativeY;
});

if (areaClassName != AreaClassCode._center) {
nodeGroup.style("opacity", 0.5);
link.style("opacity", 0.3);
}

if (!bEnableWrapping || !bDisplayContext) break;
}
}
Insert cell
function updateRender(shiftX, shiftY, areaClassName){
let nodeGroup = svgs.selectAll(".draggableNodeGroup" + areaClassName);
let link = svgs.selectAll(".link" + areaClassName);

//update
nodeGroup
.raise()
.attr("transform", function (d) {
return "translate("+ (d.x + shiftX) +","+ (d.y + shiftY)+")";
});

link.attr("x1", function (d) { return (d.source.x + shiftX); })
.attr("y1", function (d) { return (d.source.y + shiftY); })
.attr("x2", function (d) { return (d.target.x + shiftX); })
.attr("y2", function (d) { return (d.target.y + shiftY); })
.style("visibility", function (d) {
return d.visible == true ? "visible" : "hidden";
});
}
Insert cell
wrappingNode = (graph, boundaryConfig) => {
for (let node of graph.nodes) {
if (node.x > boundaryConfig.twoThirdsWidth) {
node.x -= boundaryConfig.oneThirdWidth;
while (node.x > boundaryConfig.twoThirdsWidth)
node.x -= boundaryConfig.oneThirdWidth;
} else if (node.x < boundaryConfig.oneThirdWidth) {
node.x += boundaryConfig.oneThirdWidth;
while (node.x < boundaryConfig.oneThirdWidth)
node.x += boundaryConfig.oneThirdWidth;
}

if (node.y > boundaryConfig.twoThirdsHeight) {
node.y -= boundaryConfig.oneThirdHeight;
while (node.y > boundaryConfig.twoThirdsHeight)
node.y -= boundaryConfig.oneThirdHeight;
} else if (node.y < boundaryConfig.oneThirdHeight) {
node.y += boundaryConfig.oneThirdHeight;
while (node.y < boundaryConfig.oneThirdHeight)
node.y += boundaryConfig.oneThirdHeight;
}
}
}
Insert cell
updateTorusLayout = (
bCheckWrapping,
graph,
twoEdgeWrappingGraph,
threeEdgeWrappingGraph,
boundaryConfig,
bEdgeWrappingConfig
) => {
if (bEdgeWrappingConfig != null && bEnableWrapping) {
computeTorusLayout(
graph,
twoEdgeWrappingGraph,
threeEdgeWrappingGraph,
bEdgeWrappingConfig,
boundaryConfig
);
wrappingNode(graph, boundaryConfig);
} else {
twoEdgeWrappingGraph.nodes.splice(0, twoEdgeWrappingGraph.nodes.length);
twoEdgeWrappingGraph.links.splice(0, twoEdgeWrappingGraph.links.length);
threeEdgeWrappingGraph.nodes.splice(0, threeEdgeWrappingGraph.nodes.length);
threeEdgeWrappingGraph.links.splice(0, threeEdgeWrappingGraph.links.length);
wrappingNodeLabel.splice(0, wrappingNodeLabel.length);
}

//center
updateRender(0, 0, AreaClassCode._center.toString());

if (bEnableWrapping && bDisplayContext) {
//left
updateRender(
-boundaryConfig.oneThirdWidth,
0,
AreaClassCode._left.toString()
);
//upper-left
updateRender(
-boundaryConfig.oneThirdWidth,
-boundaryConfig.oneThirdHeight,
AreaClassCode._upper_left.toString()
);
//top
updateRender(
0,
-boundaryConfig.oneThirdHeight,
AreaClassCode._top.toString()
);
//upper-right
updateRender(
boundaryConfig.oneThirdWidth,
-boundaryConfig.oneThirdHeight,
AreaClassCode._upper_right.toString()
);
//right
updateRender(
boundaryConfig.oneThirdWidth,
0,
AreaClassCode._right.toString()
);
//bottom-right
updateRender(
boundaryConfig.oneThirdWidth,
boundaryConfig.oneThirdHeight,
AreaClassCode._bottom_right.toString()
);
//bottom
updateRender(
0,
boundaryConfig.oneThirdHeight,
AreaClassCode._bottom.toString()
);
//bottom-left
updateRender(
-boundaryConfig.oneThirdWidth,
boundaryConfig.oneThirdHeight,
AreaClassCode._bottom_left.toString()
);
}

if (bEdgeWrappingConfig != null) {
//center
updateEdgeWrappingRender(
0,
0,
AreaClassCode._center.toString(),
twoEdgeWrappingGraph,
threeEdgeWrappingGraph
);

if (bEnableWrapping && bDisplayContext) {
//surrounding squares
//left
updateEdgeWrappingRender(
-boundaryConfig.oneThirdWidth,
0,
AreaClassCode._left.toString(),
twoEdgeWrappingGraph,
threeEdgeWrappingGraph
);

//upper-left
updateEdgeWrappingRender(
-boundaryConfig.oneThirdWidth,
-boundaryConfig.oneThirdHeight,
AreaClassCode._upper_left.toString(),
twoEdgeWrappingGraph,
threeEdgeWrappingGraph
);

//top
updateEdgeWrappingRender(
0,
-boundaryConfig.oneThirdHeight,
AreaClassCode._top.toString(),
twoEdgeWrappingGraph,
threeEdgeWrappingGraph
);

//upper-right
updateEdgeWrappingRender(
boundaryConfig.oneThirdWidth,
-boundaryConfig.oneThirdHeight,
AreaClassCode._upper_right.toString(),
twoEdgeWrappingGraph,
threeEdgeWrappingGraph
);

//right
updateEdgeWrappingRender(
boundaryConfig.oneThirdWidth,
0,
AreaClassCode._right.toString(),
twoEdgeWrappingGraph,
threeEdgeWrappingGraph
);

//bottom-right
updateEdgeWrappingRender(
boundaryConfig.oneThirdWidth,
boundaryConfig.oneThirdHeight,
AreaClassCode._bottom_right.toString(),
twoEdgeWrappingGraph,
threeEdgeWrappingGraph
);

//bottom
updateEdgeWrappingRender(
0,
boundaryConfig.oneThirdHeight,
AreaClassCode._bottom.toString(),
twoEdgeWrappingGraph,
threeEdgeWrappingGraph
);

//bottom-left
updateEdgeWrappingRender(
-boundaryConfig.oneThirdWidth,
boundaryConfig.oneThirdHeight,
AreaClassCode._bottom_left.toString(),
twoEdgeWrappingGraph,
threeEdgeWrappingGraph
);
}
}
}
Insert cell
updateEdgeWrappingRender = (
shiftX,
shiftY,
areaClassName,
twoEdgeWrappingGraph,
threeEdgeWrappingGraph
) => {
let twoEdgeWrappingNode = svgs
.selectAll(".twoEdgeWrappingNode" + areaClassName)
.data(twoEdgeWrappingGraph.nodes);

let twoEdgeWrappingNodeLabel = svgs
.selectAll(".wrappingNodeLabel" + areaClassName)
.data(wrappingNodeLabel);

let twoEdgeWrappingLink = svgs
.selectAll(".twoEdgeWrappingLink" + areaClassName)
.data(twoEdgeWrappingGraph.links);

//update
twoEdgeWrappingNode
.attr("cx", d => {
return d.x + shiftX;
})
.attr("cy", d => {
return d.y + shiftY;
})
.style("visibility", function(d) {
return d.visible == true ? "visible" : "hidden";
});

//enter
twoEdgeWrappingNode
.enter()
.append("circle")
.attr("class", "twoEdgeWrappingNode" + areaClassName + " cleanOnInit")
.attr("r", 4)
.attr("cx", d => {
return d.x + shiftX;
})
.attr("cy", d => {
return d.y + shiftY;
})
.attr("stroke", "black")
.style("fill", "#FFFFFF")
.style("visibility", function(d) {
return d.visible == true ? "visible" : "hidden";
});

//exit
twoEdgeWrappingNode.exit().remove();

if (bDisplayLabeledContext && areaClassName == AreaClassCode._center) {
//join
twoEdgeWrappingNodeLabel
.join("text")
.attr("class", "wrappingNodeLabel" + areaClassName + " cleanOnInit")
.attr("x", d => {
return 10 + d.x + shiftX;
})
.attr("y", d => {
return 10 + d.y + shiftY;
})
.text(d => {
if (d.name != undefined) return d.name;
})
.style("visibility", function(d) {
return d.visible == true ? "visible" : "hidden";
});
}

//update
twoEdgeWrappingLink
.attr("x1", function(d) {
return d.x1 + shiftX;
})
.attr("y1", function(d) {
return d.y1 + shiftY;
})
.attr("x2", function(d) {
return d.x2 + shiftX;
})
.attr("y2", function(d) {
return d.y2 + shiftY;
})
.lower()
.style("visibility", function(d) {
return d.visible == true ? "visible" : "hidden";
});

//enter
twoEdgeWrappingLink
.enter()
.append("line")
.attr("class", "twoEdgeWrappingLink" + areaClassName + " cleanOnInit")
.attr("x1", function(d) {
return d.x1 + shiftX;
})
.attr("y1", function(d) {
return d.y1 + shiftY;
})
.attr("x2", function(d) {
return d.x2 + shiftX;
})
.attr("y2", function(d) {
return d.y2 + shiftY;
})
.style("visibility", function(d) {
return d.visible == true ? "visible" : "hidden";
});

//exit
twoEdgeWrappingLink.exit().remove();

let threeEdgeWrappingNode = svgs
.selectAll(".threeEdgeWrappingNode" + areaClassName)
.data(threeEdgeWrappingGraph.nodes);

let threeEdgeWrappingLink = svgs
.selectAll(".threeEdgeWrappingLink" + areaClassName)
.data(threeEdgeWrappingGraph.links);

//update
threeEdgeWrappingNode
.attr("cx", d => {
return d.x + shiftX;
})
.attr("cy", d => {
return d.y + shiftY;
})
.style("visibility", function(d) {
return d.visible == true ? "visible" : "hidden";
});

//enter
threeEdgeWrappingNode
.enter()
.append("circle")
.attr("class", "threeEdgeWrappingNode" + areaClassName + " cleanOnInit")
.attr("r", 4)
.attr("cx", d => {
return d.x + shiftX;
})
.attr("cy", d => {
return d.y + shiftY;
})
.attr("stroke", "black")
.style("fill", "#FFFFFF")
.style("visibility", function(d) {
return d.visible == true ? "visible" : "hidden";
});

//exit
threeEdgeWrappingNode.exit().remove();

//update
threeEdgeWrappingLink
.attr("x1", function(d) {
return d.x1 + shiftX;
})
.attr("y1", function(d) {
return d.y1 + shiftY;
})
.attr("x2", function(d) {
return d.x2 + shiftX;
})
.attr("y2", function(d) {
return d.y2 + shiftY;
})
.style("visibility", function(d) {
return d.visible == true ? "visible" : "hidden";
});

//enter
threeEdgeWrappingLink
.enter()
.append("line")
.attr("class", "threeEdgeWrappingLink" + areaClassName + " cleanOnInit")
.attr("x1", function(d) {
return d.x1 + shiftX;
})
.attr("y1", function(d) {
return d.y1 + shiftY;
})
.attr("x2", function(d) {
return d.x2 + shiftX;
})
.attr("y2", function(d) {
return d.y2 + shiftY;
})
.style("visibility", function(d) {
return d.visible == true ? "visible" : "hidden";
});

//exit
threeEdgeWrappingLink.exit().remove();

if (areaClassName != AreaClassCode._center) {
twoEdgeWrappingNode.style("opacity", 0.3);
twoEdgeWrappingLink.style("opacity", 0.3);
threeEdgeWrappingNode.style("opacity", 0.3);
threeEdgeWrappingLink.style("opacity", 0.3);
}
}
Insert cell
AreaClassCode = ({
"_left" : 0,
"_upper_left" : 1,
"_top" : 2,
"_upper_right" : 3,
"_right" : 4,
"_bottom_left" : 5,
"_bottom" : 6,
"_bottom_right" : 7,
"_center" : 8
})
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
d3 = require("d3@5")
Insert cell
cola = require("https://ialab.it.monash.edu/~kche0088/WebCola/cola.min.js")
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more