keep if changing mind...
This commit is contained in:
139
bzGraflow.js
139
bzGraflow.js
@@ -73,7 +73,6 @@ class BZgraflow extends Buildoz{
|
||||
for(const tpl of doc.querySelectorAll('template')){
|
||||
if(tpl.id=='svg-arrows'){
|
||||
this.arrowDefs = tpl.querySelector('defs').cloneNode(true)
|
||||
console.log(this.arrowDefs)
|
||||
this.wiresContainer.appendChild(this.arrowDefs)
|
||||
} else {
|
||||
const rootEl = tpl.content.querySelector('.bzgf-node')
|
||||
@@ -157,15 +156,15 @@ class BZgraflow extends Buildoz{
|
||||
bezier(idNode1, idPort1, idNode2, idPort2, tensionMin=60) {
|
||||
const svgRect = this.wiresContainer.getBoundingClientRect()
|
||||
const node1 = this.stagedNodes[idNode1]
|
||||
const port1 = node1.ports[idPort1]
|
||||
const port1 = idPort1 ? node1.ports[idPort1] : null
|
||||
const node2 = this.stagedNodes[idNode2]
|
||||
const port2 = node2.ports[idPort2]
|
||||
if(!node1 || !node2 || !port1 || !port2) {
|
||||
console.warn('Link on bad node / port!', idNode1, idPort1, idNode2, idPort2)
|
||||
const port2 = idPort2 ? node2.ports[idPort2] : null
|
||||
if(!node1 || !node2) {
|
||||
console.warn('Link on bad node ', idNode1, idNode2)
|
||||
return('')
|
||||
}
|
||||
const bb1 = port1.el.getBoundingClientRect()
|
||||
const bb2 = port2.el.getBoundingClientRect()
|
||||
const bb1 = port1 ? port1.el.getBoundingClientRect() : node1.getBoundingClientRect()
|
||||
const bb2 = port2 ? port2.el.getBoundingClientRect() : node2.getBoundingClientRect()
|
||||
const x1 = Math.floor(bb1.x + (bb1.width/2)) - svgRect.left
|
||||
const y1 = Math.floor(bb1.y + (bb1.height/2)) - svgRect.top
|
||||
const x2 = Math.floor(bb2.x + (bb2.width/2)) - svgRect.left
|
||||
@@ -194,7 +193,7 @@ class BZgraflow extends Buildoz{
|
||||
c2y -= 1*tension
|
||||
}
|
||||
}
|
||||
return `M ${x1} ${y1} C ${c1x} ${c1y}, ${c2x} ${c2y}, ${x2} ${y2}`
|
||||
return(`M ${x1} ${y1} C ${c1x} ${c1y}, ${c2x} ${c2y}, ${x2} ${y2}`)
|
||||
}
|
||||
|
||||
autoPlace(orientation = 'horizontal', gapx = 80, gapy = 80, tween=1000){
|
||||
@@ -227,6 +226,16 @@ class BZgraflow extends Buildoz{
|
||||
layerWidths.push(totWidth)
|
||||
}
|
||||
|
||||
this.flow.longLinks = this.findLongLinks(this.flow.links)
|
||||
console.log('==============================>longLinks:', this.flow.longLinks)
|
||||
for(const link of this.flow.longLinks){
|
||||
console.log('==============================>longLink:', link)
|
||||
for(const layerIdx of link.skippedLayers){
|
||||
const nid = `longLinkPlaceHolder_${crypto.randomUUID()}`
|
||||
layers[layerIdx].push(nid)
|
||||
link.interNodes.push(nid)
|
||||
}
|
||||
}
|
||||
this.reorderLayers(layers, parents, indexes, orientation)
|
||||
|
||||
if(orientation=='horizontal'){
|
||||
@@ -235,10 +244,17 @@ class BZgraflow extends Buildoz{
|
||||
let wMax = 0
|
||||
let y = ((maxHeight - layerHeights[idx]) / 2) + gapy
|
||||
for(const nid of layer){
|
||||
const bb = this.stagedNodes[nid].getBoundingClientRect()
|
||||
wMax = (bb.width > wMax) ? bb.width : wMax
|
||||
this.moveNode(nid, x, y, tween)
|
||||
y += gapy + bb.height
|
||||
if(!nid.startsWith('longLinkPlaceHolder_')){
|
||||
const bb = this.stagedNodes[nid].getBoundingClientRect()
|
||||
wMax = (bb.width > wMax) ? bb.width : wMax
|
||||
this.moveNode(nid, x, y, tween)
|
||||
y += gapy + bb.height
|
||||
} else {
|
||||
console.log('longLinkPlaceHolder', layer)
|
||||
// Maybe center x here (but we don't have full wmax yet)
|
||||
this.moveNode(nid, x, y, tween)
|
||||
y += gapy + 50 // TODO: fix this
|
||||
}
|
||||
}
|
||||
x += wMax + gapx
|
||||
}
|
||||
@@ -248,10 +264,17 @@ class BZgraflow extends Buildoz{
|
||||
let hMax = 0
|
||||
let x = ((maxWidth - layerWidths[idx]) / 2) + gapx
|
||||
for(const nid of layer){
|
||||
const bb = this.stagedNodes[nid].getBoundingClientRect()
|
||||
hMax = (bb.height > hMax) ? bb.height : hMax
|
||||
this.moveNode(nid, x, y, tween)
|
||||
x += gapx + bb.width
|
||||
if(!nid.startsWith('longLinkPlaceHolder_')){
|
||||
const bb = this.stagedNodes[nid].getBoundingClientRect()
|
||||
hMax = (bb.height > hMax) ? bb.height : hMax
|
||||
this.moveNode(nid, x, y, tween)
|
||||
x += gapx + bb.width
|
||||
} else {
|
||||
console.log('longLinkPlaceHolder', layer)
|
||||
// Maybe center y here (but we don't have full hmax yet)
|
||||
this.moveNode(nid, x, y, tween)
|
||||
x += gapx + 50 // TODO: fix this
|
||||
}
|
||||
}
|
||||
y += hMax + gapy
|
||||
}
|
||||
@@ -298,10 +321,12 @@ class BZgraflow extends Buildoz{
|
||||
const toSwap = []
|
||||
for(let i=0; i<layer.length; i++){
|
||||
const nid1 = layer[i]
|
||||
if(nid1.startsWith('longLinkPlaceHolder_')) continue
|
||||
// some parents can be in far layers, but at least one is in the prev layer (by definition of layer)
|
||||
const pnid1 = parents[nid1].find((nid => layers[lidx-1].includes(nid)))
|
||||
for(let j=i+1; j<layer.length; j++){
|
||||
const nid2 = layer[j]
|
||||
if(nid2.startsWith('longLinkPlaceHolder_')) continue
|
||||
const pnid2 = parents[nid2].find((nid => layers[lidx-1].includes(nid)))
|
||||
const link1 = (pnid1) ? this.getLink(pnid1, nid1) : null
|
||||
const link2 = (pnid2) ? this.getLink(pnid2, nid2) : null
|
||||
@@ -345,8 +370,22 @@ class BZgraflow extends Buildoz{
|
||||
for(const wire of wires){
|
||||
const [nid1, nid2] = wire.dataset.id.split('_')
|
||||
const lnk = this.getLink(nid1, nid2)
|
||||
const path = this.bezier(nid1, lnk.from[1], nid2, lnk.to[1], this.getBZAttribute('tension'))
|
||||
wire.setAttribute('d', path)
|
||||
const longLink = this.flow.longLinks.find(item => (item.link.from[0] == lnk.from[0] && item.link.from[1] == lnk.from[1] && item.link.to[0] == lnk.to[0] && item.link.to[1] == lnk.to[1]))
|
||||
if(longLink) {
|
||||
console.log('==============================>WIRE:', wire)
|
||||
let lastNid = nid1; let lastPort = lnk.from[1];
|
||||
for(const interNode of longLink.interNodes){
|
||||
const path = this.bezier(lastNid, lastPort, interNode, null, this.getBZAttribute('tension'))
|
||||
wire.setAttribute('d', path)
|
||||
// TODO: add/draw intermediary wire
|
||||
lastNid = interNode; lastPort = null;
|
||||
}
|
||||
const path = this.bezier(lastNid, lastPort, nid2, lnk.to[1], this.getBZAttribute('tension'))
|
||||
wire.setAttribute('d', path)
|
||||
} else {
|
||||
const path = this.bezier(nid1, lnk.from[1], nid2, lnk.to[1], this.getBZAttribute('tension'))
|
||||
wire.setAttribute('d', path)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -354,7 +393,7 @@ class BZgraflow extends Buildoz{
|
||||
return(this.flow.links.find(item => ((item.from[0]==nid1) && (item.to[0]==nid2))))
|
||||
}
|
||||
|
||||
buildGraphStructures(nodes, links) {
|
||||
buildGraphStructures(nodes, links, includeLinkIndexes = false) {
|
||||
const parents = {}
|
||||
const adj = {}
|
||||
nodes.forEach(n => {
|
||||
@@ -362,14 +401,19 @@ class BZgraflow extends Buildoz{
|
||||
adj[n.id] = []
|
||||
})
|
||||
|
||||
links.forEach(link => {
|
||||
links.forEach((link, idx) => {
|
||||
const from = link.from[0]
|
||||
const to = link.to[0]
|
||||
parents[to].push(from)
|
||||
adj[from].push(to)
|
||||
const to = link.to[0]
|
||||
if(link.from[0] !== link.to[0]) { // Skip self-loops
|
||||
parents[to].push(from)
|
||||
if(includeLinkIndexes) {
|
||||
adj[from].push({ to, linkIdx: idx })
|
||||
} else {
|
||||
adj[from].push(to)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
return { parents, adj }
|
||||
return({ parents, adj })
|
||||
}
|
||||
|
||||
computeLayers(nodes, parents) {
|
||||
@@ -394,14 +438,9 @@ class BZgraflow extends Buildoz{
|
||||
}
|
||||
|
||||
hasAnyLoop(nodes, links) {
|
||||
// self-loops
|
||||
if(this.flow.links.some(l => l.from[0] === l.to[0])) return(true)
|
||||
if(links.some(l => l.from[0] === l.to[0])) return(true) // self-loops
|
||||
|
||||
// multi-node cycles
|
||||
const adj = {};
|
||||
this.flow.nodes.forEach(node => adj[node.id] = [])
|
||||
this.flow.links.forEach(link => adj[link.from[0]].push(link.to[0]))
|
||||
|
||||
const { adj } = this.buildGraphStructures(nodes, links)
|
||||
const visiting = new Set();
|
||||
const visited = new Set()
|
||||
const dfs = (nid) => {
|
||||
@@ -420,22 +459,12 @@ class BZgraflow extends Buildoz{
|
||||
visited.add(nid)
|
||||
return(false)
|
||||
}
|
||||
return(this.flow.nodes.map(n => n.id).some(dfs))
|
||||
return(nodes.map(n => n.id).some(dfs))
|
||||
}
|
||||
|
||||
findBackEdges(nodes, links) {
|
||||
// Build adjacency list with link indexes
|
||||
const adj = {};
|
||||
nodes.forEach(node => adj[node.id] = [])
|
||||
for(let [idx, link] of links.entries()){
|
||||
if(link.from[0] !== link.to[0]) { // Skip self-loops
|
||||
adj[link.from[0]].push({ to: link.to[0], linkIdx: idx })
|
||||
}
|
||||
}
|
||||
|
||||
const color = {}
|
||||
nodes.forEach(n => color[n.id] = 'white')
|
||||
|
||||
const { adj } = this.buildGraphStructures(nodes, links, true)
|
||||
const color = {}; nodes.forEach(n => color[n.id] = 'white')
|
||||
const backEdges = []
|
||||
|
||||
function dfs(u) {
|
||||
@@ -456,22 +485,32 @@ class BZgraflow extends Buildoz{
|
||||
if (color[n.id] === 'white') dfs(n.id)
|
||||
})
|
||||
|
||||
return backEdges
|
||||
return(backEdges)
|
||||
}
|
||||
|
||||
findCrossLayerLinks(links) {
|
||||
findLongLinks(links) {
|
||||
const { parents } = this.buildGraphStructures(this.flow.nodes, links)
|
||||
const layers = this.computeLayers(this.flow.nodes, parents)
|
||||
const crossLayerLinks = []
|
||||
for(const link of links){
|
||||
const from = link.from[0]
|
||||
const to = link.to[0]
|
||||
if(layers[from] !== layers[to]) {
|
||||
crossLayerLinks.push(link)
|
||||
const idx1 = layers.findIndex(layer => layer.includes(from))
|
||||
const idx2 = layers.findIndex(layer => layer.includes(to))
|
||||
if(Math.abs(idx1-idx2)>1) {
|
||||
const lowerIdx = idx1<idx2 ? idx1 : idx2
|
||||
const higherIdx = idx1>idx2 ? idx1 : idx2
|
||||
crossLayerLinks.push({
|
||||
link,
|
||||
linkIdx: link.linkIdx,
|
||||
interNodes: [],
|
||||
skippedLayers: Array.from({ length: higherIdx - lowerIdx - 1 }, (_, i) => lowerIdx + i + 1),
|
||||
})
|
||||
}
|
||||
}
|
||||
return crossLayerLinks
|
||||
return(crossLayerLinks)
|
||||
}
|
||||
}
|
||||
|
||||
Buildoz.define('graflow', BZgraflow)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user