graflow horiz + vert + fixed border & scroll issues

This commit is contained in:
STEINNI
2025-12-27 21:39:33 +00:00
parent 96f20423e3
commit 0cedc88eab
5 changed files with 256 additions and 36 deletions
+172
View File
@@ -0,0 +1,172 @@
<style>
.bzgf-node {
border-width:2px;
border-style: solid;
border-radius: 6px;
width: 160px;
height: 80px;
color: black;
text-align: center;
position: absolute;
padding: .5em;
}
.bzgf-node .title {
font-weight: bold;
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 2em;
color: white;
}
.bzgf-node .body { margin-top: 2em; }
.bzgf-node .body input {
width:4em;
font-size: .9em;
border-radius: 6px;
border: 1px solid #CCC;
}
.bzgf-node .body span{ font-size: 12px; text-align: left; line-height: 21px; }
.bzgf-node .port{
position: absolute;
height: 10px;
width: 10px;
border: 1px solid black;
border-radius: 10px;
}
.bzgf-node .port[data-type="in"] { background: #0F0; }
.bzgf-node .port[data-type="out"] { background: #FF0; }
.bzgf-node [data-direction="w"]{ left: -7px; top: 50%; }
.bzgf-node [data-direction="e"]{ right: -7px; top: 50%;}
.bzgf-node [data-direction="n"]{ top: -7px; left: 50%;}
.bzgf-node [data-direction="s"]{ bottom: -7px; left: 50%;}
.bzgf-node[data-nodetype="inc"]{
background: #DFD;
border-color: #090;
}
.bzgf-node[data-nodetype="inc"] .title{ background: #090; }
.bzgf-node[data-nodetype="wadder"]{
background: #DFD;
border-color: #090;
height:150px
}
.bzgf-node[data-nodetype="wadder"] .body{ display: grid; grid-gap: 4px; margin-left:0.5em; grid-template-columns: 1fr 1fr; align-items: center; }
.bzgf-node[data-nodetype="wadder"] .title{ background: #090; }
.bzgf-node[data-nodetype="wadder"] .port[data-id="inp1"] { top:37px; }
.bzgf-node[data-nodetype="wadder"] .port[data-id="inp2"] { top:63px; }
.bzgf-node[data-nodetype="wadder"] .port[data-id="inp3"] { top:89px; }
.bzgf-node[data-nodetype="wadder"] .port[data-id="inp4"] { top:115px; }
.bzgf-node[data-nodetype="wadder"] .port[data-id="inp5"] { top:141px; }
.bzgf-node[data-nodetype="factor"]{
background: #DDF;
border-color: #009;
}
.bzgf-node[data-nodetype="factor"] .title{ background: #009; }
.bzgf-node[data-nodetype="multiplier"]{
background: #DDF;
border-color: #009;
height:110px
}
.bzgf-node[data-nodetype="multiplier"] .body{
font-size:40px;
font-weight: bold;
align-items: center;
display: flex;
justify-content: center;
margin-top: 1em;
}
.bzgf-node[data-nodetype="multiplier"] .title{ background: #009; }
.bzgf-node[data-nodetype="multiplier"] .port[data-id="inp1"] { top:37px; }
.bzgf-node[data-nodetype="multiplier"] .port[data-id="inp2"] { top:63px; }
.bzgf-node[data-nodetype="multiplier"] .port[data-id="inp3"] { top:89px; }
.bzgf-node[data-nodetype="input"],
.bzgf-node[data-nodetype="console"]{
background: #CCC;
border-color: #555;
}
.bzgf-node[data-nodetype="input"] .title,
.bzgf-node[data-nodetype="console"] .title{ background: #555; }
.bzgf-wire{ stroke: #0AF; stroke-width: 2; }
</style>
<template>
<div class="bzgf-node" data-nodetype="inc">
<div class="title">Fixed Increment</div>
<div class="port" data-type="in" data-id="inp1" data-direction="w"></div>
<div class="port" data-type="out" data-id="out1" data-direction="e"></div>
<div class="body">
<label>Increment:</label><input name="incvalue" type="text" value="1">
</div>
</div>
</template>
<template>
<div class="bzgf-node" data-nodetype="factor">
<div class="title">Fixed factor</div>
<div class="port" data-type="in" data-id="inp1" data-direction="w"></div>
<div class="port" data-type="out" data-id="out1" data-direction="e"></div>
<div class="body">
<label>Factor:</label><input name="factvalue" type="text" value="0.5">
</div>
</div>
</template>
<template>
<div class="bzgf-node" data-nodetype="wadder">
<div class="title">Adder</div>
<div class="port" data-type="in" data-id="inp1" data-direction="w"></div>
<div class="port" data-type="in" data-id="inp2" data-direction="w"></div>
<div class="port" data-type="in" data-id="inp3" data-direction="w"></div>
<div class="port" data-type="in" data-id="inp4" data-direction="w"></div>
<div class="port" data-type="in" data-id="inp5" data-direction="w"></div>
<div class="port" data-type="out" data-id="out1" data-direction="e"></div>
<div class="body">
<div>
<span>x <input type="text" name="weight1" value="1"></span>
<span>x <input type="text" name="weight2" value="1"></span>
<span>x <input type="text" name="weight3" value="1"></span>
<span>x <input type="text" name="weight4" value="1"></span>
<span>x <input type="text" name="weight5" value="1"></span>
</div>
<div style="font-size:40px;font-weight: bold;transform: translateY(-50%);">&sum;</div>
</div>
</div>
</template>
<template>
<div class="bzgf-node" data-nodetype="multiplier">
<div class="title">Multiply</div>
<div class="port" data-type="in" data-id="inp1" data-direction="w"></div>
<div class="port" data-type="in" data-id="inp2" data-direction="w"></div>
<div class="port" data-type="in" data-id="inp3" data-direction="w"></div>
<div class="port" data-type="out" data-id="out1" data-direction="e"></div>
<div class="body">x</div>
</div>
</template>
<template>
<div class="bzgf-node" data-nodetype="console">
<div class="title">Console</div>
<div class="port" data-type="in" data-id="inp1" data-direction="w"></div>
<div class="body"></div>
</div>
</template>
<template>
<div class="bzgf-node" data-nodetype="input">
<div class="title">input</div>
<div class="port" data-type="out" data-id="out1" data-direction="e"></div>
<div class="body">
<label>Value:</label><input name="value" type="text" value="1">
</div>
</div>
</template>
+23 -2
View File
@@ -1,17 +1,38 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
<head> <head>
<title>three.js webgl - FBX loader</title> <title>graflow</title>
<meta charset="utf-8"> <meta charset="utf-8">
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0"> <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
<link type="text/css" rel="stylesheet" href="https://threejs.org/examples/main.css"> <link type="text/css" rel="stylesheet" href="https://threejs.org/examples/main.css">
<link type="text/css" rel="stylesheet" href="../../thirdparty/buildoz/buildoz.css"> <link type="text/css" rel="stylesheet" href="../../thirdparty/buildoz/buildoz.css">
<script src="../../thirdparty/buildoz/buildoz.js"></script> <script src="../../thirdparty/buildoz/buildoz.js"></script>
<script src="../../thirdparty/buildoz/bzGraflow.js"></script> <script src="../../thirdparty/buildoz/bzGraflow.js"></script>
<style>
bz-graflow{
overflow: scroll;
border: 5px ridge #4BABFF;
width: 70vw;
height: 70vh;
}
</style>
<script>
window.addEventListener('load',()=>{
const grflw = document.querySelector('bz-graflow')
document.querySelector('[data-trigger="onAutoplaceH"]').addEventListener('click',
(evt) => { grflw.autoPlace('horizontal') }
)
document.querySelector('[data-trigger="onAutoplaceV"]').addEventListener('click',
(evt) => { grflw.autoPlace('vertical') }
)
})
</script>
</head> </head>
<body> <body>
<bz-graflow flow="/app/assets/json/bzGraflow/testFlow1.json" tension="60"></bz-graflow> <bz-graflow flow="/app/assets/json/bzGraflow/testFlow1.json" tension="60"></bz-graflow>
<button data-trigger="onAutoplaceH">auto-place Horizontal</button>
<button data-trigger="onAutoplaceV">auto-place Vertical</button>
</body> </body>
</html> </html>
+6 -6
View File
@@ -10,14 +10,14 @@
"id": "aze2", "id": "aze2",
"coords": { "x": 220, "y": 10} "coords": { "x": 220, "y": 10}
}, },
{ "nodeType": "factor",
"id": "qsd2",
"coords": { "x": 470, "y": 170}
},
{ "nodeType": "factor", { "nodeType": "factor",
"id": "qsd", "id": "qsd",
"coords": { "x": 470, "y": 170}
},
{ "nodeType": "factor",
"id": "qsd2",
"coords": { "x": 470, "y": 50} "coords": { "x": 470, "y": 50}
}, },
{ "nodeType": "wadder", { "nodeType": "wadder",
"id": "wcx", "id": "wcx",
"coords": { "x": 720, "y": 50} "coords": { "x": 720, "y": 50}
@@ -39,10 +39,10 @@
{ "from": ["0000", "out1"], "to": ["aze", "inp1"] }, { "from": ["0000", "out1"], "to": ["aze", "inp1"] },
{ "from": ["aze2", "out1"], "to": ["qsd2", "inp1"] }, { "from": ["aze2", "out1"], "to": ["qsd2", "inp1"] },
{ "from": ["aze", "out1"], "to": ["qsd", "inp1"] }, { "from": ["aze", "out1"], "to": ["qsd", "inp1"] },
{ "from": ["qsd2", "out1"], "to": ["ert", "inp2"] },
{ "from": ["0000", "out1"], "to": ["aze2", "inp1"] }, { "from": ["0000", "out1"], "to": ["aze2", "inp1"] },
{ "from": ["qsd2", "out1"], "to": ["wcx", "inp2"] }, { "from": ["qsd2", "out1"], "to": ["wcx", "inp2"] },
{ "from": ["wcx", "out1"], "to": ["ert", "inp1"] }, { "from": ["wcx", "out1"], "to": ["ert", "inp1"] },
{ "from": ["qsd2", "out1"], "to": ["ert", "inp2"] },
{ "from": ["qsd", "out1"], "to": ["wcx", "inp1"] }, { "from": ["qsd", "out1"], "to": ["wcx", "inp1"] },
{ "from": ["ert", "out1"], "to": ["9999", "inp1"] } { "from": ["ert", "out1"], "to": ["9999", "inp1"] }
] ]
+11 -4
View File
@@ -188,10 +188,17 @@ bz-slidepane[side="right"] div.handle {
cursor: ew-resize; cursor: ew-resize;
} }
bz-graflow { bz-graflow {
position: relative; position: relative;
display: block; display: block;
width: 100vw; width: 100vw;
height: 100vh; height: 50vh;
box-sizing: border-box;
}
bz-graflow .bzgf-main-container{
width: 100%;
height: 100%;
position: relative;
box-sizing: border-box;
} }
bz-graflow .bzgf-wires-container, bz-graflow .bzgf-wires-container,
bz-graflow .bzgf-nodes-container{ bz-graflow .bzgf-nodes-container{
+44 -24
View File
@@ -15,13 +15,17 @@ class BZgraflow extends Buildoz{
console.warn('BZgraflow: No flow URL !?') console.warn('BZgraflow: No flow URL !?')
return return
} }
this.loadFlow(flowUrl) // Let it load async while we coat this.loadFlow(flowUrl) // Let it load async while we coat
this.mainContainer = document.createElement('div')
this.mainContainer.classList.add('bzgf-main-container')
this.nodesContainer = document.createElement('div') this.nodesContainer = document.createElement('div')
this.nodesContainer.classList.add('bzgf-nodes-container') this.nodesContainer.classList.add('bzgf-nodes-container')
this.wiresContainer = document.createElementNS('http://www.w3.org/2000/svg', 'svg') this.wiresContainer = document.createElementNS('http://www.w3.org/2000/svg', 'svg')
this.wiresContainer.setAttribute('overflow','visible')
this.wiresContainer.classList.add('bzgf-wires-container') this.wiresContainer.classList.add('bzgf-wires-container')
this.append(this.wiresContainer) this.mainContainer.append(this.wiresContainer)
this.append(this.nodesContainer) this.mainContainer.append(this.nodesContainer)
this.append(this.mainContainer)
} }
async loadFlow(url){ async loadFlow(url){
@@ -114,16 +118,17 @@ class BZgraflow extends Buildoz{
e: { x: 1, y: 0 }, e: { x: 1, y: 0 },
w: { x: -1, y: 0 }, w: { x: -1, y: 0 },
} }
const svgRect = this.wiresContainer.getBoundingClientRect()
const node1 = this.stagedNodes[idNode1] const node1 = this.stagedNodes[idNode1]
const port1 = node1.ports[idPort1] const port1 = node1.ports[idPort1]
const node2 = this.stagedNodes[idNode2] const node2 = this.stagedNodes[idNode2]
const port2 = node2.ports[idPort2] const port2 = node2.ports[idPort2]
const bb1 = port1.el.getBoundingClientRect() const bb1 = port1.el.getBoundingClientRect()
const bb2 = port2.el.getBoundingClientRect() const bb2 = port2.el.getBoundingClientRect()
const x1 = Math.floor(bb1.x + (bb1.width/2)) const x1 = Math.floor(bb1.x + (bb1.width/2)) - svgRect.left
const y1 = Math.floor(bb1.y + (bb1.height/2)) const y1 = Math.floor(bb1.y + (bb1.height/2)) - svgRect.top
const x2 = Math.floor(bb2.x + (bb2.width/2)) const x2 = Math.floor(bb2.x + (bb2.width/2)) - svgRect.left
const y2 = Math.floor(bb2.y + (bb2.height/2)) const y2 = Math.floor(bb2.y + (bb2.height/2)) - svgRect.top
const dist = Math.abs(x2 - x1) + Math.abs(y2 - y1) const dist = Math.abs(x2 - x1) + Math.abs(y2 - y1)
let tension = dist * 0.4 let tension = dist * 0.4
if(tension < tensionMin) tension = tensionMin if(tension < tensionMin) tension = tensionMin
@@ -137,11 +142,8 @@ class BZgraflow extends Buildoz{
} }
autoPlace(orientation = 'horizontal', gapx = 80, gapy = 30){ autoPlace(orientation = 'horizontal', gapx = 80, gapy = 30){
if(!['horizontal', 'vertical'].includes(orientation)) return
const parents = {} const parents = {}
const adj = {} const adj = {}
this.flow.nodes.forEach(n => { this.flow.nodes.forEach(n => {
parents[n.id] = [] parents[n.id] = []
adj[n.id] = [] adj[n.id] = []
@@ -155,33 +157,51 @@ class BZgraflow extends Buildoz{
}) })
const layers = this.computeLayers(this.flow.nodes, parents) const layers = this.computeLayers(this.flow.nodes, parents)
let maxHeight = 0 let maxHeight = 0; let maxWidth = 0
const layerHeights = [] const layerHeights = []; const layerWidths = [];
const indexes = {} // Todo: a const indexes = {} // Todo: a
for(const layer of layers){ for(const layer of layers){
let totHeight = 0 let totHeight = 0; let totWidth = 0
for(const [idx, nid] of layer.entries()){ for(const [idx, nid] of layer.entries()){
const bb = this.stagedNodes[nid].getBoundingClientRect() const bb = this.stagedNodes[nid].getBoundingClientRect()
totHeight += bb.height+gapy totHeight += bb.height + gapy
totWidth += bb.width + gapx
indexes[nid] = idx indexes[nid] = idx
} }
if(totHeight>maxHeight) maxHeight = totHeight if(totHeight>maxHeight) maxHeight = totHeight
layerHeights.push(totHeight) layerHeights.push(totHeight)
if(totWidth>maxWidth) maxWidth = totWidth
layerWidths.push(totWidth)
} }
this.reorderLayers(layers, parents, indexes) this.reorderLayers(layers, parents, indexes)
let x = gapx if(orientation=='horizontal'){
for(const [idx, layer] of layers.entries()){ let x = gapx
let wMax = 0 for(const [idx, layer] of layers.entries()){
let y = ((maxHeight - layerHeights[idx]) / 2) + gapy let wMax = 0
for(const nid of layer){ let y = ((maxHeight - layerHeights[idx]) / 2) + gapy
const bb = this.stagedNodes[nid].getBoundingClientRect() for(const nid of layer){
wMax = (bb.width > wMax) ? bb.width : wMax const bb = this.stagedNodes[nid].getBoundingClientRect()
this.moveNode(nid, x, y, 1000) wMax = (bb.width > wMax) ? bb.width : wMax
y += gapy + bb.height this.moveNode(nid, x, y, 1000)
y += gapy + bb.height
}
x += wMax + gapx
} }
x += wMax + gapx } else if(orientation=='vertical'){
let y = gapy
for(const [idx, layer] of layers.entries()){
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.width : hMax
this.moveNode(nid, x, y, 1000)
x += gapx + bb.width
}
y += hMax + gapy
}
} }
} }