Spaces:
Running
Running
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 => {
|