This commit is contained in:
锦恢 2024-12-23 00:04:11 +08:00
parent d1ab16c5d2
commit 4586b5b19c
6 changed files with 87 additions and 25 deletions

13
design/netlist.drawio Normal file
View File

@ -0,0 +1,13 @@
<mxfile host="65bd71144e">
<diagram id="SfLhgSYCHkdHWK2D13we" name="第 1 页">
<mxGraphModel dx="588" dy="624" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="827" pageHeight="1169" math="0" shadow="0">
<root>
<mxCell id="0"/>
<mxCell id="1" parent="0"/>
<mxCell id="2" value="" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#6a00ff;fontColor=#ffffff;strokeColor=#3700CC;" vertex="1" parent="1">
<mxGeometry x="240" y="290" width="120" height="60" as="geometry"/>
</mxCell>
</root>
</mxGraphModel>
</diagram>
</mxfile>

View File

@ -1,7 +1,6 @@
<template>
<!-- 渲染区域 -->
<!-- <Render></Render> -->
<div id="netlist"></div>
<Render></Render>
<!-- 右侧工具合集 -->
<RightNav></RightNav>
</template>

View File

@ -17,5 +17,8 @@ defineComponent({ name: 'netlist-render' });
height: 100vh;
width: calc(100vw - var(--right-nav-width));
overflow: hidden;
display: flex;
justify-content: center;
align-items: center;
}
</style>

View File

@ -148,6 +148,8 @@
/**
* @typedef ElkNode
* @property {string} id 节点的唯一标识符
* @property {string} [renderName] node 展示的名字布局算法前生成
* @property {'port' | 'cell' | 'cellPort'} [renderType] 渲染的类型
* @property {ElkNode[]} children 当前节点的内部
* @property {ElkPort[]} ports 当前节点的端口
* @property {ElkEdge[]} edges 当前节点的连线
@ -188,8 +190,9 @@
/**
* @typedef ElkSection
* @property {ElkPoint} startPoint
* @property {ElkPoint} endPoint
* @property {ElkPoint} startPoint 起点
* @property {ElkPoint} endPoint 终点
* @property {ElkPoint[]} bendPoints 中间经过的点
* @property {string} id edge id
* @property {string} incomingShape source 实体的 id
* @property {string} outgoingShape target 实体的 id

View File

@ -25,6 +25,10 @@ export class NetlistRender {
*/
this.elkGraph = {
id: 'root',
layoutOptions: {
'elk.algorithm': 'layered',
'layered.nodePlacement.strategy': 'SIDE_BASED'
},
children: [],
edges: []
};
@ -67,10 +71,7 @@ export class NetlistRender {
* @returns {Promise<ElkNode>}
*/
async createLayout() {
const elk = new ELK({
// 分层算法,使得生成的边和坐标轴平行
algorithm: 'layered'
});
const elk = new ELK();
const graph = this.elkGraph;
const layoutGraph = await elk.layout(graph);
console.log(layoutGraph);
@ -93,8 +94,8 @@ export class NetlistRender {
// 遍历计算布局进行创建
const svg = d3.select(container).append('svg')
.attr('width', this.renderWidth)
.attr('height', this.renderHeight);
.attr('width', virtualWidth)
.attr('height', virtualHeight);
await this.renderEntity(svg, computedLayout, ratio);
await this.renderLine(svg, computedLayout, ratio);
@ -122,7 +123,10 @@ export class NetlistRender {
y: node.y,
width: node.width,
height: node.height,
fill: 'green'
fill: '#2D323B',
text: node.renderName,
rx: 3,
ry: 3
});
// 如果存在 port绘制 port
@ -132,7 +136,10 @@ export class NetlistRender {
y: cellPort.y + node.y,
width: cellPort.width,
height: cellPort.height,
fill: 'blue'
fill: '#CB81DA',
text: '',
rx: 0,
ry: 0
});
}
}
@ -146,8 +153,23 @@ export class NetlistRender {
.attr('width', data => data.width)
.attr('height', data => data.height)
.attr('fill', d => d.fill)
.attr('stroke', 'white')
.attr('stroke-width', 2);
.attr('stroke', '#CB81DA')
.attr('stroke-width', 2)
.attr('rx', d => d.rx)
.attr('ry', d => d.ry);
svg.selectAll('text')
.data(squares)
.enter()
.append('text')
.attr('x', data => data.x + data.width / 2) // 文本的 x 坐标(居中)
.attr('y', data => data.y + data.height / 2) // 文本的 y 坐标(居中)
.attr('dominant-baseline', 'middle') // 文本垂直居中
.attr('text-anchor', 'middle') // 文本水平居中
.attr('fill', 'white') // 文本颜色
.attr('font-size', '12px')
.text(data => data.text); // 设置文本内容
}
/**
@ -160,16 +182,25 @@ export class NetlistRender {
const lines = [];
for (const edge of computedLayout.edges) {
for (const section of edge.sections || []) {
const points = [];
points.push(section.startPoint);
for (const point of section.bendPoints || []) {
points.push(point);
}
points.push(section.endPoint);
for (let i = 0; i < points.length - 1; ++ i) {
lines.push({
x1: section.startPoint.x,
y1: section.startPoint.y,
x2: section.endPoint.x,
y2: section.endPoint.y,
x1: points[i].x,
y1: points[i].y,
x2: points[i + 1].x,
y2: points[i + 1].y,
strokeWidth: 2,
color: 'white'
color: 'var(--foreground)'
});
}
}
}
svg.selectAll('line')
.data(lines)
@ -180,6 +211,6 @@ export class NetlistRender {
.attr('x2', data => data.x2)
.attr('y2', data => data.y2)
.attr('stroke-width', data => data.strokeWidth)
.attr('stroke', data => data.color)
.attr('stroke', data => data.color);
}
}

View File

@ -6,7 +6,7 @@ import { CELL_LIBS, ModuleTree } from "./yosys";
export const LAYOUT_CONSTANT = {
PORT_WIDTH: 50,
PORT_HEIGHT: 50,
PORT_HEIGHT: 20,
INSTANTIATION_WIDTH: 50,
INSTANTIATION_HEIGHT: 50,
CONSTANT_WIDTH: 50,
@ -68,10 +68,18 @@ export class Module {
for (const name of this.moduleTree.nameToPort.keys()) {
const port = this.moduleTree.nameToPort.get(name);
const direction = port.direction === 'input' ? 'LEFT': 'RIGHT';
const node = {
id: port.id,
renderName: name,
renderType: 'port',
type: port.direction,
width: LAYOUT_CONSTANT.PORT_WIDTH,
height: LAYOUT_CONSTANT.PORT_HEIGHT,
properties: {
'elk.layered.nodePlacement.side': direction
}
};
nodes.push(node);
@ -103,6 +111,8 @@ export class Module {
const portSide = connection.direction === 'input' ? ELK_DIRECTION.LEFT: ELK_DIRECTION.RIGHT;
ports.push({
id: connection.id,
renderName: connectionName,
renderType: 'cellPort',
width: LAYOUT_CONSTANT.CELL_PORT_WIDTH,
height: LAYOUT_CONSTANT.CELL_PORT_HEIGHT,
properties: {
@ -113,6 +123,8 @@ export class Module {
const node = {
id: cell.id,
renderName: cell.type,
renderType: 'cell',
width,
height,
ports,
@ -156,6 +168,7 @@ export class Module {
// 如果是常数,需要先创建代表常数的节点,常数一定是器件的输入,而非输出
const node = {
id,
name: wireId,
width: LAYOUT_CONSTANT.CONSTANT_WIDTH,
height: LAYOUT_CONSTANT.CONSTANT_HEIGHT,
layoutOptions: {