完成例化的展开
This commit is contained in:
parent
9886b8c881
commit
0bcb475812
@ -5,7 +5,7 @@
|
||||
<div :class="expandManage.expandTagClass"></div>
|
||||
</span>
|
||||
<span :class="`iconfont ${makeIconClass(module)}`"></span>
|
||||
 {{ module.name }}
|
||||
 {{ renderName }}
|
||||
</div>
|
||||
|
||||
<!-- 渲染这个 module scope 有的组件 -->
|
||||
@ -27,7 +27,8 @@
|
||||
<!-- 例化模块 -->
|
||||
<modules
|
||||
v-for="(cell, index) in cells"
|
||||
:module="cell.view"
|
||||
:module="cell.view"
|
||||
:render-name="cell.renderName"
|
||||
:key="index"
|
||||
></modules>
|
||||
</div>
|
||||
@ -39,7 +40,7 @@
|
||||
/* eslint-disable */
|
||||
import { globalLookup } from '@/hook/global';
|
||||
import { ModuleView } from '@/hook/render/yosys';
|
||||
import { defineComponent, reactive } from 'vue';
|
||||
import { defineComponent, reactive, computed } from 'vue';
|
||||
|
||||
defineComponent({ name: 'modules' });
|
||||
|
||||
@ -47,11 +48,18 @@ const props = defineProps({
|
||||
module: {
|
||||
type: ModuleView,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
renderName: {
|
||||
type: String
|
||||
}
|
||||
});
|
||||
|
||||
const module = props.module;
|
||||
|
||||
const renderName = computed(() => {
|
||||
return props.renderName ? props.renderName : module.name
|
||||
});
|
||||
|
||||
// 当前 scope 的例化模块下的
|
||||
const ports = [];
|
||||
const cells = [];
|
||||
@ -63,20 +71,25 @@ for (const portName of module.nameToPort.keys()) {
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
for (const cellName of module.nameToCell.keys()) {
|
||||
const cell = module.nameToCell.get(cellName);
|
||||
if (cell.view === module) {
|
||||
|
||||
if (cell.belongModuleView === module) {
|
||||
// 防止递归
|
||||
continue;
|
||||
}
|
||||
if (cell.isInstantiation) {
|
||||
if (cell.isInstantiation) {
|
||||
cells.push({
|
||||
name: cellName,
|
||||
view: cell.belongModuleView
|
||||
view: cell.belongModuleView,
|
||||
renderName: `${cellName} (${cell.belongModuleView.name})`
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
function clickItem() {
|
||||
|
||||
}
|
||||
|
@ -255,3 +255,8 @@ import { Module } from "./render/layout";
|
||||
* @typedef DragConnection
|
||||
* @property {d3.Selection} selection
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef ElkMakerConfig
|
||||
* @param {string} [parentId]
|
||||
*/
|
@ -48,11 +48,17 @@ export class CellRender {
|
||||
const id2manager = this.id2manager;
|
||||
const _this = this;
|
||||
|
||||
|
||||
console.log(data);
|
||||
|
||||
|
||||
let cellSelections = this.parentSelection.selectAll('svg')
|
||||
.data(data)
|
||||
.enter()
|
||||
.append(data => {
|
||||
const element = data.element;
|
||||
console.log(data);
|
||||
|
||||
element.setAttribute('x', data.x);
|
||||
element.setAttribute('y', data.y);
|
||||
if (globalSetting.renderAnimation) {
|
||||
|
@ -11,6 +11,7 @@ import { ConnectionRender } from './connection';
|
||||
import { WireRender } from './wire';
|
||||
import { pinkLog, redLog } from '../utils';
|
||||
import { treeviewData } from '@/components/treeview/tree';
|
||||
import { dotConnect } from './yosys';
|
||||
|
||||
export class NetlistRender {
|
||||
/**
|
||||
@ -20,6 +21,24 @@ export class NetlistRender {
|
||||
* @param {number} renderWidth
|
||||
*/
|
||||
constructor() {
|
||||
this.defaultLayoutOptions = {
|
||||
// ...
|
||||
'org.eclipse.elk.layered.spacing.nodeNodeBetweenLayers': 35,
|
||||
// node 之间的最小间距
|
||||
'elk.spacing.nodeNode': 35,
|
||||
// layered 算法布局,尽可能让全体布局从左向右
|
||||
'elk.algorithm': 'layered',
|
||||
// layered 算法的风格
|
||||
'elk.layered.layering.strategy': 'NETWORK_SIMPLEX',
|
||||
// ...
|
||||
'elk.layered.considerModelOrder.longEdgeStrategy': 'DUMMY_NODE_UNDER',
|
||||
// edge 的生成采用正交路由算法
|
||||
'elk.edgeRouting': 'ORTHOGONAL',
|
||||
// 指定 layered 算法的方向为从左到右
|
||||
'elk.direction': 'RIGHT',
|
||||
// 激活 node 分区功能,可以通过 partitioning.partition 赋予 id 的方式给 node 进行分组
|
||||
'elk.partitioning.activate': true
|
||||
}
|
||||
/**
|
||||
* @type {ElkGraph}
|
||||
*/
|
||||
@ -27,24 +46,7 @@ export class NetlistRender {
|
||||
id: 'root',
|
||||
children: [],
|
||||
edges: [],
|
||||
layoutOptions: {
|
||||
// ...
|
||||
'org.eclipse.elk.layered.spacing.nodeNodeBetweenLayers': 35,
|
||||
// node 之间的最小间距
|
||||
'elk.spacing.nodeNode': 35,
|
||||
// layered 算法布局,尽可能让全体布局从左向右
|
||||
'elk.algorithm': 'layered',
|
||||
// layered 算法的风格
|
||||
'elk.layered.layering.strategy': 'NETWORK_SIMPLEX',
|
||||
// ...
|
||||
'elk.layered.considerModelOrder.longEdgeStrategy': 'DUMMY_NODE_UNDER',
|
||||
// edge 的生成采用正交路由算法
|
||||
'elk.edgeRouting': 'ORTHOGONAL',
|
||||
// 指定 layered 算法的方向为从左到右
|
||||
'elk.direction': 'RIGHT',
|
||||
// 激活 node 分区功能,可以通过 partitioning.partition 赋予 id 的方式给 node 进行分组
|
||||
'elk.partitioning.activate': true
|
||||
}
|
||||
layoutOptions: this.defaultLayoutOptions
|
||||
};
|
||||
|
||||
/**
|
||||
@ -187,9 +189,6 @@ export class NetlistRender {
|
||||
this.renderWidth = element.clientWidth;
|
||||
this.registerResizeHandler(element);
|
||||
|
||||
// 根据 height 进行放缩(可以通过设置进行调整)
|
||||
const ratio = this.renderHeight / virtualHeight;
|
||||
|
||||
// 遍历计算布局进行创建
|
||||
const svg = d3.select(container)
|
||||
.selectAll('svg')
|
||||
@ -203,13 +202,14 @@ export class NetlistRender {
|
||||
console.log(computedLayout);
|
||||
|
||||
// 生成连接
|
||||
await this.renderLine(g, computedLayout, ratio);
|
||||
await this.renderLine(g, computedLayout);
|
||||
|
||||
// 生成实体
|
||||
await this.renderEntity(g, computedLayout, ratio);
|
||||
await this.renderEntity(g, computedLayout);
|
||||
|
||||
// svg 挂载为全局注册的 selection
|
||||
this.selection = svg;
|
||||
this.g = g;
|
||||
|
||||
// 注册平移和缩放
|
||||
this.registerRenderTransform(g);
|
||||
@ -229,9 +229,8 @@ export class NetlistRender {
|
||||
* @description 绘制实体
|
||||
* @param {d3.Selection} parentSelection
|
||||
* @param {ElkNode} computedLayout
|
||||
* @param {number} ratio
|
||||
*/
|
||||
async renderEntity(parentSelection, computedLayout, ratio) {
|
||||
async renderEntity(parentSelection, computedLayout) {
|
||||
// node 可能是如下的几类
|
||||
// - module 的 port
|
||||
// - 器件(基础器件 & 例化模块)
|
||||
@ -251,7 +250,8 @@ export class NetlistRender {
|
||||
const skin = skinManager.querySkin(node.renderName);
|
||||
if (skin) {
|
||||
// 具有 skin 的器件
|
||||
this.cellRender.addAsD3DataItem(node, skin.meta.svgDoc.documentElement);
|
||||
const element = skin.meta.svgDoc.documentElement.cloneNode(true);
|
||||
this.cellRender.addAsD3DataItem(node, element);
|
||||
} else {
|
||||
if (node.renderType === 'port') {
|
||||
this.portRender.addAsD3DataItem(node);
|
||||
@ -267,19 +267,20 @@ export class NetlistRender {
|
||||
}
|
||||
}
|
||||
|
||||
this.portRender.render();
|
||||
this.instantiationRender.render();
|
||||
this.cellRender.render();
|
||||
this.connectionRender.render();
|
||||
const ports = this.portRender.render();
|
||||
const instances = this.instantiationRender.render();
|
||||
const cells = this.cellRender.render();
|
||||
const connections = this.connectionRender.render();
|
||||
|
||||
return { ports, instances, cells, connections };
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 绘制连线
|
||||
* @param {d3.Selection} parentSelection
|
||||
* @param {ElkNode} computedLayout
|
||||
* @param {number} ratio
|
||||
*/
|
||||
async renderLine(parentSelection, computedLayout, ratio) {
|
||||
async renderLine(parentSelection, computedLayout) {
|
||||
this.wireRender = new WireRender(parentSelection, this);
|
||||
|
||||
for (const edge of computedLayout.edges) {
|
||||
@ -455,16 +456,167 @@ export class NetlistRender {
|
||||
|
||||
/**
|
||||
* @description 展开例化模块
|
||||
* @param {d3.Selection} instanceSelection
|
||||
*/
|
||||
expandInstance() {
|
||||
async expandInstance(instanceSelection) {
|
||||
const data = instanceSelection.datum();
|
||||
const elkNode = data._self;
|
||||
const moduleName = elkNode.renderName;
|
||||
const module = this.nameToModule.get(moduleName);
|
||||
|
||||
function addNodeIdPrefix(nodes) {
|
||||
for (const node of nodes) {
|
||||
node.id = dotConnect(elkNode.name, node.id);
|
||||
for (const port of node.ports || []) {
|
||||
port.id = dotConnect(elkNode.name, port.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function addEdgeIdPrefix(edges) {
|
||||
for (const edge of edges) {
|
||||
edge.id = dotConnect(elkNode.name, edge.id);
|
||||
edge.source = dotConnect(elkNode.name, edge.source);
|
||||
edge.sourcePort = dotConnect(elkNode.name, edge.sourcePort);
|
||||
edge.target = dotConnect(elkNode.name, edge.target);
|
||||
edge.targetPort = dotConnect(elkNode.name, edge.targetPort);
|
||||
}
|
||||
}
|
||||
|
||||
const portNodes = module.makeNetsElkNodes();
|
||||
const cellNodes = module.makeCellsElkNodes();
|
||||
const [constantNodes, connectionEdges] = module.makeConnectionElkNodes();
|
||||
|
||||
// 增加前缀
|
||||
addNodeIdPrefix(portNodes);
|
||||
addNodeIdPrefix(cellNodes);
|
||||
addNodeIdPrefix(constantNodes);
|
||||
addEdgeIdPrefix(connectionEdges);
|
||||
|
||||
elkNode.children = [];
|
||||
elkNode.edges = [];
|
||||
elkNode.layoutOptions = this.defaultLayoutOptions;
|
||||
|
||||
elkNode.children.push(...portNodes);
|
||||
elkNode.children.push(...cellNodes);
|
||||
elkNode.children.push(...constantNodes);
|
||||
elkNode.edges.push(...connectionEdges);
|
||||
|
||||
const start = performance.now();
|
||||
|
||||
// 更新布局
|
||||
await this.elk.layout(this.elkGraph);
|
||||
const computedLayout = this.elkGraph;
|
||||
const timecost = (performance.now() - start).toFixed(3);
|
||||
|
||||
pinkLog(`expandInstance 布局计算耗时 ${timecost} ms`);
|
||||
console.log(computedLayout);
|
||||
|
||||
const svg = this.selection;
|
||||
const g = this.g;
|
||||
|
||||
g.selectAll('*').remove();
|
||||
|
||||
// 开始递归地进行渲染
|
||||
const renderStack = [
|
||||
{
|
||||
parentSelection: g,
|
||||
layout: computedLayout
|
||||
}
|
||||
];
|
||||
|
||||
while (renderStack.length > 0) {
|
||||
const s = renderStack.pop();
|
||||
const parentSelection = s.parentSelection;
|
||||
const layout = s.layout;
|
||||
await this.renderLine(parentSelection, layout);
|
||||
const { instances } = await this.renderEntity(parentSelection, layout);
|
||||
const id2selection = new Map();
|
||||
instances.each(function(data) {
|
||||
const selection = d3.select(this);
|
||||
id2selection.set(data.id, selection);
|
||||
});
|
||||
|
||||
// 将需要渲染子图的部分扔进渲染器
|
||||
for (const node of layout.children || []) {
|
||||
const subChildren = node.children || [];
|
||||
|
||||
if (subChildren.length > 0) {
|
||||
const selection = id2selection.get(node.id);
|
||||
renderStack.push({
|
||||
parentSelection: selection,
|
||||
layout: node
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @description 关闭例化模块
|
||||
* @param {d3.Selection} instanceSelection
|
||||
*/
|
||||
closeInstance() {
|
||||
async collapseInstance(instanceSelection) {
|
||||
const data = instanceSelection.datum();
|
||||
const elkNode = data._self;
|
||||
const moduleName = elkNode.renderName;
|
||||
const module = this.nameToModule.get(moduleName);
|
||||
|
||||
|
||||
|
||||
elkNode.children = [];
|
||||
elkNode.edges = [];
|
||||
// 重新计算 elkNode 的宽度和高度
|
||||
|
||||
|
||||
const start = performance.now();
|
||||
|
||||
// 更新布局
|
||||
await this.elk.layout(this.elkGraph);
|
||||
const computedLayout = this.elkGraph;
|
||||
const timecost = (performance.now() - start).toFixed(3);
|
||||
|
||||
pinkLog(`collapseInstance 布局计算耗时 ${timecost} ms`);
|
||||
console.log(computedLayout);
|
||||
|
||||
const svg = this.selection;
|
||||
const g = this.g;
|
||||
|
||||
g.selectAll('*').remove();
|
||||
|
||||
// 开始递归地进行渲染
|
||||
const renderStack = [
|
||||
{
|
||||
parentSelection: g,
|
||||
layout: computedLayout
|
||||
}
|
||||
];
|
||||
|
||||
while (renderStack.length > 0) {
|
||||
const s = renderStack.pop();
|
||||
const parentSelection = s.parentSelection;
|
||||
const layout = s.layout;
|
||||
await this.renderLine(parentSelection, layout);
|
||||
const { instances } = await this.renderEntity(parentSelection, layout);
|
||||
const id2selection = new Map();
|
||||
instances.each(function(data) {
|
||||
const selection = d3.select(this);
|
||||
id2selection.set(data.id, selection);
|
||||
});
|
||||
|
||||
// 将需要渲染子图的部分扔进渲染器
|
||||
for (const node of layout.children || []) {
|
||||
const subChildren = node.children || [];
|
||||
|
||||
if (subChildren.length > 0) {
|
||||
const selection = id2selection.get(node.id);
|
||||
renderStack.push({
|
||||
parentSelection: selection,
|
||||
layout: node
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -93,7 +93,8 @@ export class InstantiationRender {
|
||||
portnames,
|
||||
rx: 3,
|
||||
ry: 3,
|
||||
expandText: '📌'
|
||||
expandText: '📌',
|
||||
_self: node
|
||||
});
|
||||
}
|
||||
|
||||
@ -114,7 +115,10 @@ export class InstantiationRender {
|
||||
.attr('y', LAYOUT_CONSTANT.INSTANCE_TITLE_HEIGHT)
|
||||
.attr('width', data => data.width)
|
||||
.attr('height', data => data.height - LAYOUT_CONSTANT.INSTANCE_TITLE_HEIGHT)
|
||||
.attr('fill', d => d.fill);
|
||||
.attr('fill', d => {
|
||||
const children = d._self.children || [];
|
||||
return children.length > 0 ? 'rgba(203, 129, 218, 0.1)' : d.fill;
|
||||
});
|
||||
|
||||
|
||||
let texts = instantiationSelections.append('text')
|
||||
@ -138,7 +142,15 @@ export class InstantiationRender {
|
||||
.attr('fill', 'var(--main-color)');
|
||||
})
|
||||
.on('click', function(event) {
|
||||
|
||||
const selection = d3.select(this);
|
||||
const d = selection.datum();
|
||||
const children = d._self.children || [];
|
||||
|
||||
if (children.length > 0) {
|
||||
rootRender.collapseInstance(selection);
|
||||
} else {
|
||||
rootRender.expandInstance(selection);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
@ -202,8 +202,6 @@ export class Module {
|
||||
x: 0,
|
||||
y: yOffset
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
|
||||
// 计算右侧的
|
||||
|
@ -45,7 +45,8 @@ export class ModuleView {
|
||||
}
|
||||
}
|
||||
|
||||
for (const [cellName, rawCell] of Object.entries(rawModule.cells)) {
|
||||
|
||||
for (const [cellName, rawCell] of Object.entries(rawModule.cells)) {
|
||||
const cell = new Cell(this, cellName, rawCell);
|
||||
this._nameToCell.set(cellName, cell);
|
||||
// 构建映射
|
||||
@ -128,7 +129,7 @@ export class Cell {
|
||||
*/
|
||||
constructor(view, name, rawCell) {
|
||||
this.view = view;
|
||||
this.name = name;
|
||||
this.name = name;
|
||||
this.rawCell = rawCell;
|
||||
|
||||
/**
|
||||
|
@ -67,6 +67,9 @@ class SkinMeta {
|
||||
// 颜色替换
|
||||
// 填充颜色
|
||||
svgString = svgString.replace(/#279BB0/g, 'var(--main-dark-color)');
|
||||
|
||||
svgString = svgString.replace(/#41C9A0/g, 'var(--main-dark-color)');
|
||||
|
||||
// 边缘颜色
|
||||
svgString = svgString.replace(/#000000/g, 'var(--main-color)');
|
||||
// 字体颜色
|
||||
|
Loading…
x
Reference in New Issue
Block a user