Bundle group added to .yml generation and canvas
This commit is contained in:
+85
-37
@@ -22,6 +22,7 @@
|
||||
const DEFAULT_CANVAS_SIZE = { width: 5000, height: 5000 };
|
||||
// Base visual diameter and hit area used for port and anchor handles.
|
||||
const PORT_NODE_SIZE = 30;
|
||||
const FREE_WIRES_BUNDLE_GROUP = 'free_wires';
|
||||
const PORT_LABEL_MIN_CHARS = 5;
|
||||
const PORT_LABEL_CHAR_WIDTH = 7;
|
||||
const PORT_LABEL_HORIZONTAL_PADDING = 12;
|
||||
@@ -137,6 +138,26 @@
|
||||
return (technology.xsections && technology.xsections[xsection]) || technology.xsections.strip || {};
|
||||
};
|
||||
|
||||
const cleanBundleGroupName = (value) => String(value ?? '')
|
||||
.trim()
|
||||
.replace(/\s+/g, '_')
|
||||
.replace(/[^A-Za-z0-9_.-]/g, '_')
|
||||
.replace(/_+/g, '_')
|
||||
.replace(/^[._-]+|[._-]+$/g, '');
|
||||
|
||||
const normalizeBundleGroupName = (value, fallback = FREE_WIRES_BUNDLE_GROUP) => {
|
||||
const cleaned = cleanBundleGroupName(value);
|
||||
if (cleaned) return cleaned;
|
||||
const fallbackText = fallback === null || fallback === undefined ? '' : String(fallback);
|
||||
return cleanBundleGroupName(fallbackText) || (fallbackText === '' ? '' : FREE_WIRES_BUNDLE_GROUP);
|
||||
};
|
||||
|
||||
const freeWireBundleGroupName = (xsection, defaultXsection) => {
|
||||
const defaultName = normalizeBundleGroupName(defaultXsection || FALLBACK_TECHNOLOGY_MANIFEST.defaults.xsection || 'strip', 'strip');
|
||||
const currentName = normalizeBundleGroupName(xsection || defaultXsection || defaultName, defaultName);
|
||||
return currentName === defaultName ? FREE_WIRES_BUNDLE_GROUP : `${FREE_WIRES_BUNDLE_GROUP}_${currentName}`;
|
||||
};
|
||||
|
||||
// Normalize route settings so every edge has xsection, family, width, radius, and bend type.
|
||||
const createRouteSettings = (manifest, overrides) => {
|
||||
const technology = getTechnologyManifest(manifest);
|
||||
@@ -150,6 +171,7 @@
|
||||
width: Number((overrides && overrides.width) ?? xsectionInfo.default_width ?? defaults.width ?? 0.45),
|
||||
radius: Number((overrides && overrides.radius) ?? xsectionInfo.default_radius ?? defaults.radius ?? 10),
|
||||
routing_type: (overrides && overrides.routing_type) || defaults.routing_type || 'euler_bend',
|
||||
bundle_group: (overrides && (overrides.bundle_group ?? overrides.bundleGroup)) || '',
|
||||
widthEdited: Boolean(overrides && overrides.widthEdited)
|
||||
};
|
||||
};
|
||||
@@ -809,7 +831,8 @@
|
||||
}
|
||||
const entries = [];
|
||||
Array.from({ length: portNumber }, (_, index) => {
|
||||
const y = elementPortOffset(index, portNumber, pitch);
|
||||
const defaultSingleAnchor = portNumber === 1;
|
||||
const y = defaultSingleAnchor ? -PORT_NODE_SIZE / 2 : elementPortOffset(index, portNumber, pitch);
|
||||
entries.push([`a${index + 1}`, { x: 0, y, a: 180, width }]);
|
||||
entries.push([`b${index + 1}`, { x: 0, y, a: 0, width }]);
|
||||
});
|
||||
@@ -1004,54 +1027,76 @@ ${pinLines}`;
|
||||
const nodeMap = {};
|
||||
nodes.forEach(n => { nodeMap[n.id] = n; });
|
||||
|
||||
let linksYaml = '';
|
||||
if (edges.length > 0) {
|
||||
const linkLines = edges.map(edge => {
|
||||
const sourceNode = nodeMap[edge.source];
|
||||
const targetNode = nodeMap[edge.target];
|
||||
const sourceName = sourceNode ? (sourceNode.data.componentDisplayName || sourceNode.id) : edge.source;
|
||||
const targetName = targetNode ? (targetNode.data.componentDisplayName || targetNode.id) : edge.target;
|
||||
const fromPort = sourceNode && sourceNode.data && sourceNode.data.elementType
|
||||
? getElementPinName(sourceNode, edge.sourceHandle)
|
||||
: edge.sourceHandle || 'unknown';
|
||||
const toPort = targetNode && targetNode.data && targetNode.data.elementType
|
||||
? getElementPinName(targetNode, edge.targetHandle)
|
||||
: edge.targetHandle || 'unknown';
|
||||
const route = createRouteSettings(manifest, edge.data && edge.data.route);
|
||||
const routeWidth = getRouteEndpointWidth(sourceNode, edge.sourceHandle)
|
||||
?? getRouteEndpointWidth(targetNode, edge.targetHandle)
|
||||
?? route.width;
|
||||
const storedPoints = Array.isArray(edge.data && edge.data.points) ? edge.data.points : [];
|
||||
const points = storedPoints.length >= 2 ? getEdgeRoutePoints(edge, nodeMap) : [];
|
||||
const pointsYaml = points.length > 0
|
||||
? `\n points:\n${points.map(point => ` - x: ${Number(point.x || 0).toFixed(1)}\n y: ${canvasToLayoutY(point.y).toFixed(1)}`).join('\n')}`
|
||||
: '';
|
||||
const isFreeRoute = Boolean(edge.data && edge.data.freeRoute) || (!sourceNode && !targetNode && points.length >= 2);
|
||||
if (isFreeRoute) {
|
||||
return ` - id: ${toYamlScalar(edge.id)}
|
||||
const groups = new Map();
|
||||
let primaryFreeWireXsection = '';
|
||||
const freeWireGroupForRoute = (route) => {
|
||||
const xsectionName = normalizeBundleGroupName(route.xsection, 'strip');
|
||||
if (!primaryFreeWireXsection) {
|
||||
primaryFreeWireXsection = xsectionName;
|
||||
return FREE_WIRES_BUNDLE_GROUP;
|
||||
}
|
||||
return xsectionName === primaryFreeWireXsection
|
||||
? FREE_WIRES_BUNDLE_GROUP
|
||||
: `${FREE_WIRES_BUNDLE_GROUP}_${xsectionName}`;
|
||||
};
|
||||
|
||||
edges.forEach(edge => {
|
||||
const sourceNode = nodeMap[edge.source];
|
||||
const targetNode = nodeMap[edge.target];
|
||||
const sourceName = sourceNode ? (sourceNode.data.componentDisplayName || sourceNode.id) : edge.source;
|
||||
const targetName = targetNode ? (targetNode.data.componentDisplayName || targetNode.id) : edge.target;
|
||||
const fromPort = sourceNode && sourceNode.data && sourceNode.data.elementType
|
||||
? getElementPinName(sourceNode, edge.sourceHandle)
|
||||
: edge.sourceHandle || 'unknown';
|
||||
const toPort = targetNode && targetNode.data && targetNode.data.elementType
|
||||
? getElementPinName(targetNode, edge.targetHandle)
|
||||
: edge.targetHandle || 'unknown';
|
||||
const route = createRouteSettings(manifest, edge.data && edge.data.route);
|
||||
const routeWidth = getRouteEndpointWidth(sourceNode, edge.sourceHandle)
|
||||
?? getRouteEndpointWidth(targetNode, edge.targetHandle)
|
||||
?? route.width;
|
||||
const storedPoints = Array.isArray(edge.data && edge.data.points) ? edge.data.points : [];
|
||||
const points = storedPoints.length >= 2 ? getEdgeRoutePoints(edge, nodeMap) : [];
|
||||
const pointsYaml = points.length > 0
|
||||
? `\n points:\n${points.map(point => ` - x: ${Number(point.x || 0).toFixed(1)}\n y: ${canvasToLayoutY(point.y).toFixed(1)}`).join('\n')}`
|
||||
: '';
|
||||
const isFreeRoute = Boolean(edge.data && edge.data.freeRoute) || (!sourceNode && !targetNode && points.length >= 2);
|
||||
const linkYaml = isFreeRoute
|
||||
? ` - id: ${toYamlScalar(edge.id)}
|
||||
xsection: ${route.xsection}
|
||||
family: ${route.family}
|
||||
width: ${Number(routeWidth)}
|
||||
radius: ${Number(route.radius)}
|
||||
routing_type: ${route.routing_type}${pointsYaml}`;
|
||||
}
|
||||
return ` - from: ${sourceName}:${fromPort}
|
||||
routing_type: ${route.routing_type}${pointsYaml}`
|
||||
: ` - from: ${sourceName}:${fromPort}
|
||||
to: ${targetName}:${toPort}
|
||||
xsection: ${route.xsection}
|
||||
family: ${route.family}
|
||||
width: ${Number(routeWidth)}
|
||||
radius: ${Number(route.radius)}
|
||||
routing_type: ${route.routing_type}${pointsYaml}`;
|
||||
});
|
||||
linksYaml = linkLines.join('\n');
|
||||
}
|
||||
const routeGroupName = normalizeBundleGroupName(route.bundle_group, '');
|
||||
const groupName = routeGroupName || freeWireGroupForRoute(route);
|
||||
if (!groups.has(groupName)) {
|
||||
groups.set(groupName, {
|
||||
xsection: route.xsection,
|
||||
family: route.family,
|
||||
routing_type: route.routing_type,
|
||||
links: []
|
||||
});
|
||||
}
|
||||
groups.get(groupName).links.push(linkYaml);
|
||||
});
|
||||
|
||||
const groupsYaml = Array.from(groups.entries()).map(([groupName, group]) => ` ${groupName}:
|
||||
xsection: ${group.xsection}
|
||||
family: ${group.family}
|
||||
routing_type: ${group.routing_type}
|
||||
links:
|
||||
${group.links.join('\n')}`).join('\n');
|
||||
|
||||
return `# 3. Bundles (Grouped links for multi-bus/parallel routing)
|
||||
bundles:
|
||||
output_bus:
|
||||
routing_type: euler_bend
|
||||
links:
|
||||
${linksYaml}`;
|
||||
bundles:${groupsYaml ? `\n${groupsYaml}` : ' {}'}`;
|
||||
};
|
||||
|
||||
// Return the center point of a node when a more precise port point is unavailable.
|
||||
@@ -1252,6 +1297,7 @@ ${linksYaml}`;
|
||||
BASIC_COMPONENTS,
|
||||
DEFAULT_FORGE_ARGUMENTS,
|
||||
FALLBACK_TECHNOLOGY_MANIFEST,
|
||||
FREE_WIRES_BUNDLE_GROUP,
|
||||
canvasToLayoutY,
|
||||
layoutToCanvasY,
|
||||
createForgeArguments,
|
||||
@@ -1259,6 +1305,8 @@ ${linksYaml}`;
|
||||
updateRouteField,
|
||||
updateRouteXsection,
|
||||
routeStyleForSettings,
|
||||
normalizeBundleGroupName,
|
||||
freeWireBundleGroupName,
|
||||
findSameTypeRouteCrossing,
|
||||
findSameFamilyRouteCrossing,
|
||||
isForgeComponent,
|
||||
|
||||
Reference in New Issue
Block a user