pagraphcontrol/components/graph/layout-engine.js
2019-08-11 00:39:28 +03:00

124 lines
2.4 KiB
JavaScript

const {
filter,
} = require('ramda');
const { size } = require('../../constants/view');
const plusMinus = require('../../utils/plus-minus');
const margin = size / 10;
const step = size + (2 * margin);
const offsetY = 1080 / 2;
const centerColumnsCount = 5;
module.exports = class LayoutEngine {
constructor() {
Object.assign(this, {
size,
margin,
});
}
nodesIntersect(a, b) {
if (a.x === undefined || a.y === undefined || b.x === undefined || b.y === undefined) {
return undefined;
}
return (((a.x - size - margin) < b.x) && (b.x < (a.x + size + margin)))
&& (((a.y - size - margin) < b.y) && (b.y < (a.y + size + margin)));
}
calculatePosition(node) {
return node;
}
adjustNodes(nodes) {
const targetClientsColumnHeight = Math.round(filter(
x => x.type === 'sink' || x.type === 'source',
nodes,
).length * 0.75);
const estimatedColumnHeights = {
total: 0,
get(k) {
return this[k] || 0;
},
inc(k) {
this[k] = this[k] || 0;
this[k] += 1;
this.total += 1;
return this[k];
},
};
const nodeColumn = node => Math.round(node.x / step) - 2;
const unpositionedNodes = nodes.filter(node => {
if (node.type === 'satellite') {
return false;
}
if (node.x !== undefined) {
estimatedColumnHeights.inc(nodeColumn(node));
return false;
}
return true;
});
unpositionedNodes.forEach(node => {
if (node.type === 'source') {
node.x = 0 * step;
} else if (node.type === 'sink') {
node.x = 8 * step;
} else {
let targetCol = node.index % centerColumnsCount;
if (estimatedColumnHeights.get(2) < targetClientsColumnHeight) {
targetCol = 2;
}
node.x = (2 * step) + (targetCol * step);
}
const columnHeight = estimatedColumnHeights.inc(nodeColumn(node));
const direction = Math.sign(plusMinus(node.index));
node.y = offsetY + (direction * (Math.floor(columnHeight / 2) - 1) * (size + margin));
let intersected = true;
let iterations = 0;
while (intersected && iterations < 10) {
intersected = false;
for (const otherNode of nodes) {
if (otherNode.type === 'satellite') {
continue;
}
if (otherNode === node) {
continue;
}
iterations += 1;
if (this.nodesIntersect(node, otherNode)) {
node.y += direction * (size + margin);
intersected = true;
}
}
}
});
return nodes;
}
getPositionForNode(node) {
return this.calculatePosition(node);
}
};