Updated
This commit is contained in:
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user