import kiwi from 'kiwi.js'
const kvar = kiwi.Variable
const kexp = kiwi.Expression
const keq = kiwi.Operator.Eq
const kge = kiwi.Operator.Ge
const kle = kiwi.Operator.Le
const kstrong = kiwi.Strength.strong
const xConstSep = 55
const yConstSep = 40
class TensorflowLayout {
constructor(options) {
this.options = options
this.cy = options.cy
this.eles = options.eles
this.nodes = options.eles.nodes()
this.edges = options.eles.edges()
let bb = options.boundingBox || {
x1: 0,
y1: 0,
w: this.cy.width(),
h: this.cy.height()
}
if (bb.x2 === undefined){ bb.x2 = bb.x1 + bb.w; }
if (bb.w === undefined){ bb.w = bb.x2 - bb.x1; }
if (bb.y2 === undefined){ bb.y2 = bb.y1 + bb.h; }
if (bb.h === undefined){ bb.h = bb.y2 - bb.y1; }
this.bb = bb
}
run() {
let nodes = this.nodes.filter('node[op != "Const"]')
let eles = nodes.union(nodes.edgesTo(nodes))
let dagre = eles.layout({
name: 'dagre',
fit: false,
})
dagre.run()
let consts = this.nodes.filter('node[op = "Const"]')
let solver = new kiwi.Solver()
let xs = consts.map(_ => new kvar())
let ys = consts.map(_ => new kvar())
let idx = {}
consts.forEach((n, i) => idx[n.id()] = i)
consts.forEach(n => {
let nid = idx[n.id()]
solver.addEditVariable(xs[nid], kstrong)
solver.addEditVariable(ys[nid], kstrong)
})
consts.outgoers('node').forEach(n => {
let nx = n.position('x')
let ny = n.position('y')
let nw = n.layoutDimensions().w
let nh = n.layoutDimensions().h
let prevConsts = n.incomers().filter('node[op = "Const"]')
let prevConstsSize = prevConsts.length
prevConsts.forEach((m, i) => {
let mid = idx[m.id()]
let leftx = nx - (xConstSep + nw / 2);
let rightx = nx + (xConstSep + nw / 2);
let conflicts = nodes.some(o => {
if (o.isParent() && !o.hasClass('cy-expand-collapse-collapsed-node')) {
return false;
}
let ox = o.position('x')
let oy = o.position('y')
let ow = o.layoutDimensions().w
let oh = o.layoutDimensions().h
return ox - ow / 2 <= leftx &&
leftx <= ox + ow / 2 &&
oy - oh / 2 <= ny &&
ny <= oy + oh / 2
})
if (!conflicts) {
solver.createConstraint(xs[mid], keq, leftx)
} else {
solver.createConstraint(xs[mid], keq, rightx)
}
if (i > 0) {
let pid = idx[prevConsts[i - 1].id()]
solver.createConstraint(ys[mid], kge, new kexp(ys[pid], yConstSep))
}
})
let ymean = prevConsts.map(m => [1 / prevConstsSize, ys[idx[m.id()]]])
solver.createConstraint(new kexp(...ymean), keq, ny)
})
solver.updateVariables()
consts.layoutPositions(this, this.options, function(node){
node = typeof node === "object" ? node : this
return {
x: xs[idx[node.id()]].value(),
y: ys[idx[node.id()]].value(),
}
})
return this
}
}
export default (cytoscape) => {
cytoscape('layout', 'tensorflow', TensorflowLayout)
}