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); } };