Spaces:
Paused
Paused
Update static/canvas.js
Browse files- static/canvas.js +93 -14
static/canvas.js
CHANGED
|
@@ -97,19 +97,60 @@ function clearCanvas() {
|
|
| 97 |
|
| 98 |
// Create nodes and connections from parsed data
|
| 99 |
function createNodesFromParsedData(parsedNodes, parsedConnections) {
|
| 100 |
-
const
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 101 |
parsedNodes.forEach(nodeData => {
|
| 102 |
-
|
| 103 |
-
|
| 104 |
-
|
| 105 |
-
|
| 106 |
-
|
|
|
|
|
|
|
| 107 |
}
|
| 108 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 109 |
const level = nodeData.level || 0;
|
| 110 |
-
|
| 111 |
-
|
| 112 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 113 |
|
| 114 |
const node = createNode(
|
| 115 |
x,
|
|
@@ -126,6 +167,7 @@ function createNodesFromParsedData(parsedNodes, parsedConnections) {
|
|
| 126 |
nodes.push(node);
|
| 127 |
layer.add(node);
|
| 128 |
});
|
|
|
|
| 129 |
layer.draw();
|
| 130 |
autoConnect();
|
| 131 |
saveNodes();
|
|
@@ -441,7 +483,7 @@ function autoConnect() {
|
|
| 441 |
|
| 442 |
const sortedNodes = [...nodes].sort((a, b) => {
|
| 443 |
if (a.data.level !== b.data.level) return a.data.level - b.data.level;
|
| 444 |
-
return a.data.
|
| 445 |
});
|
| 446 |
|
| 447 |
const hierarchy = {};
|
|
@@ -539,7 +581,7 @@ function updateProgram() {
|
|
| 539 |
function reconstructProgram() {
|
| 540 |
const sortedNodes = [...nodes].sort((a, b) => {
|
| 541 |
if (a.data.level !== b.data.level) return a.data.level - b.data.level;
|
| 542 |
-
return a.data.
|
| 543 |
});
|
| 544 |
|
| 545 |
let program = '';
|
|
@@ -556,8 +598,8 @@ function reconstructProgram() {
|
|
| 556 |
// Add a manual node
|
| 557 |
function addNode() {
|
| 558 |
const node = createNode(
|
| 559 |
-
|
| 560 |
-
|
| 561 |
'Function',
|
| 562 |
'function',
|
| 563 |
['in1'],
|
|
@@ -573,6 +615,43 @@ function addNode() {
|
|
| 573 |
saveNodes();
|
| 574 |
}
|
| 575 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 576 |
// Update spline connections when nodes move
|
| 577 |
function updateConnections() {
|
| 578 |
layer.find('Shape').forEach(shape => {
|
|
|
|
| 97 |
|
| 98 |
// Create nodes and connections from parsed data
|
| 99 |
function createNodesFromParsedData(parsedNodes, parsedConnections) {
|
| 100 |
+
const columns = {
|
| 101 |
+
imports: { x: 50, y: 50, count: 0 }, // Column for imports
|
| 102 |
+
global: { x: 250, y: 50, count: 0 }, // Column for global scope
|
| 103 |
+
functions: {} // Columns for functions
|
| 104 |
+
};
|
| 105 |
+
|
| 106 |
+
// First pass: Assign function nodes to columns
|
| 107 |
parsedNodes.forEach(nodeData => {
|
| 108 |
+
if (nodeData.type === 'function') {
|
| 109 |
+
const functionId = nodeData.id || `Function_${Object.keys(columns.functions).length + 1}`;
|
| 110 |
+
columns.functions[functionId] = {
|
| 111 |
+
x: 450 + Object.keys(columns.functions).length * 200,
|
| 112 |
+
y: 50,
|
| 113 |
+
count: 0
|
| 114 |
+
};
|
| 115 |
}
|
| 116 |
+
});
|
| 117 |
+
|
| 118 |
+
// Second pass: Create nodes with column-based positioning
|
| 119 |
+
parsedNodes.forEach(nodeData => {
|
| 120 |
+
const parentPath = nodeData.parent_path || 'global';
|
| 121 |
const level = nodeData.level || 0;
|
| 122 |
+
let x, y;
|
| 123 |
+
|
| 124 |
+
if (nodeData.type === 'import') {
|
| 125 |
+
// Imports column
|
| 126 |
+
x = columns.imports.x;
|
| 127 |
+
y = columns.imports.y + columns.imports.count * 80;
|
| 128 |
+
columns.imports.count++;
|
| 129 |
+
} else if (nodeData.type === 'function') {
|
| 130 |
+
// Function column
|
| 131 |
+
const functionId = nodeData.id;
|
| 132 |
+
x = columns.functions[functionId].x;
|
| 133 |
+
y = columns.functions[functionId].y + columns.functions[functionId].count * 80;
|
| 134 |
+
columns.functions[functionId].count++;
|
| 135 |
+
} else if (parentPath !== 'global' && parentPath.includes('Function')) {
|
| 136 |
+
// Child of a function
|
| 137 |
+
const functionId = parentPath.split(' -> ')[0];
|
| 138 |
+
if (columns.functions[functionId]) {
|
| 139 |
+
x = columns.functions[functionId].x;
|
| 140 |
+
y = columns.functions[functionId].y + columns.functions[functionId].count * 80;
|
| 141 |
+
columns.functions[functionId].count++;
|
| 142 |
+
} else {
|
| 143 |
+
// Fallback to global if function not found
|
| 144 |
+
x = columns.global.x;
|
| 145 |
+
y = columns.global.y + columns.global.count * 80;
|
| 146 |
+
columns.global.count++;
|
| 147 |
+
}
|
| 148 |
+
} else {
|
| 149 |
+
// Global scope (non-import, non-function)
|
| 150 |
+
x = columns.global.x;
|
| 151 |
+
y = columns.global.y + columns.global.count * 80;
|
| 152 |
+
columns.global.count++;
|
| 153 |
+
}
|
| 154 |
|
| 155 |
const node = createNode(
|
| 156 |
x,
|
|
|
|
| 167 |
nodes.push(node);
|
| 168 |
layer.add(node);
|
| 169 |
});
|
| 170 |
+
|
| 171 |
layer.draw();
|
| 172 |
autoConnect();
|
| 173 |
saveNodes();
|
|
|
|
| 483 |
|
| 484 |
const sortedNodes = [...nodes].sort((a, b) => {
|
| 485 |
if (a.data.level !== b.data.level) return a.data.level - b.data.level;
|
| 486 |
+
return a.data.y - b.data.y; // Sort by y within columns
|
| 487 |
});
|
| 488 |
|
| 489 |
const hierarchy = {};
|
|
|
|
| 581 |
function reconstructProgram() {
|
| 582 |
const sortedNodes = [...nodes].sort((a, b) => {
|
| 583 |
if (a.data.level !== b.data.level) return a.data.level - b.data.level;
|
| 584 |
+
return a.data.y - b.data.y; // Sort by y within columns
|
| 585 |
});
|
| 586 |
|
| 587 |
let program = '';
|
|
|
|
| 598 |
// Add a manual node
|
| 599 |
function addNode() {
|
| 600 |
const node = createNode(
|
| 601 |
+
250, // Add to global column
|
| 602 |
+
50 + nodes.filter(n => n.data.parent_path === 'global').length * 80,
|
| 603 |
'Function',
|
| 604 |
'function',
|
| 605 |
['in1'],
|
|
|
|
| 615 |
saveNodes();
|
| 616 |
}
|
| 617 |
|
| 618 |
+
// Update spline connections when nodes move
|
| 619 |
+
function createSplineConnection(fromNode, fromPortId, toNode, toPortId) {
|
| 620 |
+
const fromPort = fromNode.data.outputs.find(p => p.id === fromPortId);
|
| 621 |
+
const toPort = toNode.data.inputs.find(p => p.id === toPortId);
|
| 622 |
+
if (!fromPort || !toPort) return;
|
| 623 |
+
|
| 624 |
+
const startX = fromNode.x() + fromPort.circle.x();
|
| 625 |
+
const startY = fromNode.y() + fromPort.circle.y();
|
| 626 |
+
const endX = toNode.x() + toPort.circle.x();
|
| 627 |
+
const endY = toNode.y() + toPort.circle.y();
|
| 628 |
+
|
| 629 |
+
const control1X = startX + (endX - startX) / 3;
|
| 630 |
+
const control1Y = startY;
|
| 631 |
+
const control2X = startX + 2 * (endX - startX) / 3;
|
| 632 |
+
const control2Y = endY;
|
| 633 |
+
|
| 634 |
+
const spline = new Konva.Shape({
|
| 635 |
+
sceneFunc: function(context, shape) {
|
| 636 |
+
context.beginPath();
|
| 637 |
+
context.moveTo(startX, startY);
|
| 638 |
+
context.bezierCurveTo(control1X, control1Y, control2X, control2Y, endX, endY);
|
| 639 |
+
context.fillStrokeShape(shape);
|
| 640 |
+
},
|
| 641 |
+
stroke: 'black',
|
| 642 |
+
strokeWidth: 2
|
| 643 |
+
});
|
| 644 |
+
|
| 645 |
+
spline.data = {
|
| 646 |
+
fromNodeId: fromNode.data.id,
|
| 647 |
+
fromPortId: fromPortId,
|
| 648 |
+
toNodeId: toNode.data.id,
|
| 649 |
+
toPortId: toPortId
|
| 650 |
+
};
|
| 651 |
+
layer.add(spline);
|
| 652 |
+
layer.draw();
|
| 653 |
+
}
|
| 654 |
+
|
| 655 |
// Update spline connections when nodes move
|
| 656 |
function updateConnections() {
|
| 657 |
layer.find('Shape').forEach(shape => {
|