This commit is contained in:
2026-05-28 17:53:41 +08:00
parent 48555f5686
commit e6e9e13cf2
22 changed files with 1743 additions and 186 deletions
+170
View File
@@ -43,11 +43,99 @@
notes: ''
};
const FALLBACK_TECHNOLOGY_MANIFEST = {
routing_types: ['euler_bend', 'standard_bend'],
defaults: {
xsection: 'strip',
family: 'optical',
width: 0.45,
radius: 10,
routing_type: 'euler_bend'
},
xsections: {
strip: { family: 'optical', default_width: 0.45, default_radius: 10 },
rib_low: { family: 'optical', default_width: 0.45, default_radius: 10 },
metal_1: { family: 'electrical', default_width: 5, default_radius: 10 },
metal_2: { family: 'electrical', default_width: 5, default_radius: 10 }
}
};
const createForgeArguments = (overrides) => ({
...DEFAULT_FORGE_ARGUMENTS,
...(overrides || {})
});
const getTechnologyManifest = (manifest) => manifest || FALLBACK_TECHNOLOGY_MANIFEST;
const getXsectionInfo = (xsection, manifest) => {
const technology = getTechnologyManifest(manifest);
return (technology.xsections && technology.xsections[xsection]) || technology.xsections.strip || {};
};
const createRouteSettings = (manifest, overrides) => {
const technology = getTechnologyManifest(manifest);
const defaults = technology.defaults || FALLBACK_TECHNOLOGY_MANIFEST.defaults;
const xsection = (overrides && overrides.xsection) || defaults.xsection || 'strip';
const xsectionInfo = getXsectionInfo(xsection, technology);
const family = (overrides && overrides.family) || xsectionInfo.family || defaults.family || 'optical';
return {
xsection,
family,
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',
widthEdited: Boolean(overrides && overrides.widthEdited)
};
};
const updateRouteField = (route, key, value, manifest) => {
const current = createRouteSettings(manifest, route);
const numericFields = new Set(['width', 'radius']);
const nextValue = numericFields.has(key) ? Number(value || 0) : value;
return {
...current,
[key]: nextValue,
widthEdited: key === 'width' ? true : current.widthEdited
};
};
const updateRouteXsection = (route, xsection, manifest) => {
const technology = getTechnologyManifest(manifest);
const current = createRouteSettings(technology, route);
const xsectionInfo = getXsectionInfo(xsection, technology);
const next = {
...current,
xsection,
family: xsectionInfo.family || current.family
};
if (!current.widthEdited) {
next.width = Number(xsectionInfo.default_width ?? current.width);
}
next.radius = Number(xsectionInfo.default_radius ?? current.radius);
return next;
};
const routeStyleForSettings = (route, selected) => {
const settings = createRouteSettings(null, route);
const palette = {
strip: '#38bdf8',
rib_low: '#22c55e',
metal_1: '#f59e0b',
metal_2: '#f97316'
};
const electrical = settings.family === 'electrical';
const strokeWidth = electrical ? 3.5 : 2.4;
return {
type: electrical ? 'step' : 'smoothstep',
style: {
stroke: palette[settings.xsection] || palette.strip,
strokeWidth: selected ? strokeWidth + 1.2 : strokeWidth,
strokeDasharray: electrical ? '8 5' : undefined,
filter: selected ? 'drop-shadow(0 0 5px rgba(255,255,255,0.45))' : undefined
}
};
};
const isForgeComponent = (componentName) => componentName === FORGE_COMPONENT_LABEL || componentName === FORGE_COMPONENT_TYPE;
const normalizeAngle = (angle) => {
@@ -272,12 +360,93 @@
return `elements:\n${lines.join('\n')}`;
};
const buildBundlesYaml = (page, manifest) => {
const { nodes = [], edges = [] } = page || {};
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 = edge.sourceHandle || 'unknown';
const toPort = edge.targetHandle || 'unknown';
const route = createRouteSettings(manifest, edge.data && edge.data.route);
return ` - from: ${sourceName}:${fromPort}
to: ${targetName}:${toPort}
xsection: ${route.xsection}
family: ${route.family}
width: ${Number(route.width)}
radius: ${Number(route.radius)}
routing_type: ${route.routing_type}`;
});
linksYaml = linkLines.join('\n');
}
return `# 3. Bundles (Grouped links for multi-bus/parallel routing)
bundles:
output_bus:
routing_type: euler_bend
links:
${linksYaml}`;
};
const getNodeCenter = (node) => {
if (!node) return null;
return {
x: Number((node.position && node.position.x) || 0),
y: Number((node.position && node.position.y) || 0)
};
};
const orientation = (a, b, c) => {
const value = (b.y - a.y) * (c.x - b.x) - (b.x - a.x) * (c.y - b.y);
if (Math.abs(value) < 1e-9) return 0;
return value > 0 ? 1 : 2;
};
const segmentsIntersect = (p1, q1, p2, q2) => {
if (!p1 || !q1 || !p2 || !q2) return false;
const o1 = orientation(p1, q1, p2);
const o2 = orientation(p1, q1, q2);
const o3 = orientation(p2, q2, p1);
const o4 = orientation(p2, q2, q1);
return o1 !== o2 && o3 !== o4;
};
const findSameFamilyRouteCrossing = (candidateEdge, existingEdges, nodeMap, manifest) => {
const candidateRoute = createRouteSettings(manifest, candidateEdge.data && candidateEdge.data.route);
const candidateStart = getNodeCenter(nodeMap[candidateEdge.source]);
const candidateEnd = getNodeCenter(nodeMap[candidateEdge.target]);
for (const edge of existingEdges || []) {
if (!edge || edge.id === candidateEdge.id) continue;
if (edge.source === candidateEdge.source || edge.source === candidateEdge.target || edge.target === candidateEdge.source || edge.target === candidateEdge.target) continue;
const route = createRouteSettings(manifest, edge.data && edge.data.route);
if (route.family !== candidateRoute.family) continue;
const start = getNodeCenter(nodeMap[edge.source]);
const end = getNodeCenter(nodeMap[edge.target]);
if (segmentsIntersect(candidateStart, candidateEnd, start, end)) {
return { conflictEdge: edge, family: route.family };
}
}
return null;
};
return {
FORGE_COMPONENT_LABEL,
FORGE_COMPONENT_TYPE,
ELEMENT_COMPONENTS,
DEFAULT_FORGE_ARGUMENTS,
FALLBACK_TECHNOLOGY_MANIFEST,
createForgeArguments,
createRouteSettings,
updateRouteField,
updateRouteXsection,
routeStyleForSettings,
findSameFamilyRouteCrossing,
isForgeComponent,
normalizeAngle,
portSideFromAngle,
@@ -287,6 +456,7 @@
buildInstancesYaml,
buildPageComponentPorts,
buildCanvasPortsYaml,
buildBundlesYaml,
buildPortsYaml,
buildElementsYaml,
buildSettingsYaml,