完成例化的展开
This commit is contained in:
parent
9886b8c881
commit
0bcb475812
@ -5,7 +5,7 @@
|
|||||||
<div :class="expandManage.expandTagClass"></div>
|
<div :class="expandManage.expandTagClass"></div>
|
||||||
</span>
|
</span>
|
||||||
<span :class="`iconfont ${makeIconClass(module)}`"></span>
|
<span :class="`iconfont ${makeIconClass(module)}`"></span>
|
||||||
 {{ module.name }}
|
 {{ renderName }}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 渲染这个 module scope 有的组件 -->
|
<!-- 渲染这个 module scope 有的组件 -->
|
||||||
@ -28,6 +28,7 @@
|
|||||||
<modules
|
<modules
|
||||||
v-for="(cell, index) in cells"
|
v-for="(cell, index) in cells"
|
||||||
:module="cell.view"
|
:module="cell.view"
|
||||||
|
:render-name="cell.renderName"
|
||||||
:key="index"
|
:key="index"
|
||||||
></modules>
|
></modules>
|
||||||
</div>
|
</div>
|
||||||
@ -39,7 +40,7 @@
|
|||||||
/* eslint-disable */
|
/* eslint-disable */
|
||||||
import { globalLookup } from '@/hook/global';
|
import { globalLookup } from '@/hook/global';
|
||||||
import { ModuleView } from '@/hook/render/yosys';
|
import { ModuleView } from '@/hook/render/yosys';
|
||||||
import { defineComponent, reactive } from 'vue';
|
import { defineComponent, reactive, computed } from 'vue';
|
||||||
|
|
||||||
defineComponent({ name: 'modules' });
|
defineComponent({ name: 'modules' });
|
||||||
|
|
||||||
@ -47,11 +48,18 @@ const props = defineProps({
|
|||||||
module: {
|
module: {
|
||||||
type: ModuleView,
|
type: ModuleView,
|
||||||
required: true
|
required: true
|
||||||
|
},
|
||||||
|
renderName: {
|
||||||
|
type: String
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const module = props.module;
|
const module = props.module;
|
||||||
|
|
||||||
|
const renderName = computed(() => {
|
||||||
|
return props.renderName ? props.renderName : module.name
|
||||||
|
});
|
||||||
|
|
||||||
// 当前 scope 的例化模块下的
|
// 当前 scope 的例化模块下的
|
||||||
const ports = [];
|
const ports = [];
|
||||||
const cells = [];
|
const cells = [];
|
||||||
@ -63,20 +71,25 @@ for (const portName of module.nameToPort.keys()) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
for (const cellName of module.nameToCell.keys()) {
|
for (const cellName of module.nameToCell.keys()) {
|
||||||
const cell = module.nameToCell.get(cellName);
|
const cell = module.nameToCell.get(cellName);
|
||||||
if (cell.view === module) {
|
|
||||||
|
if (cell.belongModuleView === module) {
|
||||||
// 防止递归
|
// 防止递归
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (cell.isInstantiation) {
|
if (cell.isInstantiation) {
|
||||||
cells.push({
|
cells.push({
|
||||||
name: cellName,
|
name: cellName,
|
||||||
view: cell.belongModuleView
|
view: cell.belongModuleView,
|
||||||
|
renderName: `${cellName} (${cell.belongModuleView.name})`
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
function clickItem() {
|
function clickItem() {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -255,3 +255,8 @@ import { Module } from "./render/layout";
|
|||||||
* @typedef DragConnection
|
* @typedef DragConnection
|
||||||
* @property {d3.Selection} selection
|
* @property {d3.Selection} selection
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef ElkMakerConfig
|
||||||
|
* @param {string} [parentId]
|
||||||
|
*/
|
@ -48,11 +48,17 @@ export class CellRender {
|
|||||||
const id2manager = this.id2manager;
|
const id2manager = this.id2manager;
|
||||||
const _this = this;
|
const _this = this;
|
||||||
|
|
||||||
|
|
||||||
|
console.log(data);
|
||||||
|
|
||||||
|
|
||||||
let cellSelections = this.parentSelection.selectAll('svg')
|
let cellSelections = this.parentSelection.selectAll('svg')
|
||||||
.data(data)
|
.data(data)
|
||||||
.enter()
|
.enter()
|
||||||
.append(data => {
|
.append(data => {
|
||||||
const element = data.element;
|
const element = data.element;
|
||||||
|
console.log(data);
|
||||||
|
|
||||||
element.setAttribute('x', data.x);
|
element.setAttribute('x', data.x);
|
||||||
element.setAttribute('y', data.y);
|
element.setAttribute('y', data.y);
|
||||||
if (globalSetting.renderAnimation) {
|
if (globalSetting.renderAnimation) {
|
||||||
|
@ -11,6 +11,7 @@ import { ConnectionRender } from './connection';
|
|||||||
import { WireRender } from './wire';
|
import { WireRender } from './wire';
|
||||||
import { pinkLog, redLog } from '../utils';
|
import { pinkLog, redLog } from '../utils';
|
||||||
import { treeviewData } from '@/components/treeview/tree';
|
import { treeviewData } from '@/components/treeview/tree';
|
||||||
|
import { dotConnect } from './yosys';
|
||||||
|
|
||||||
export class NetlistRender {
|
export class NetlistRender {
|
||||||
/**
|
/**
|
||||||
@ -20,14 +21,7 @@ export class NetlistRender {
|
|||||||
* @param {number} renderWidth
|
* @param {number} renderWidth
|
||||||
*/
|
*/
|
||||||
constructor() {
|
constructor() {
|
||||||
/**
|
this.defaultLayoutOptions = {
|
||||||
* @type {ElkGraph}
|
|
||||||
*/
|
|
||||||
this.elkGraph = {
|
|
||||||
id: 'root',
|
|
||||||
children: [],
|
|
||||||
edges: [],
|
|
||||||
layoutOptions: {
|
|
||||||
// ...
|
// ...
|
||||||
'org.eclipse.elk.layered.spacing.nodeNodeBetweenLayers': 35,
|
'org.eclipse.elk.layered.spacing.nodeNodeBetweenLayers': 35,
|
||||||
// node 之间的最小间距
|
// node 之间的最小间距
|
||||||
@ -45,6 +39,14 @@ export class NetlistRender {
|
|||||||
// 激活 node 分区功能,可以通过 partitioning.partition 赋予 id 的方式给 node 进行分组
|
// 激活 node 分区功能,可以通过 partitioning.partition 赋予 id 的方式给 node 进行分组
|
||||||
'elk.partitioning.activate': true
|
'elk.partitioning.activate': true
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* @type {ElkGraph}
|
||||||
|
*/
|
||||||
|
this.elkGraph = {
|
||||||
|
id: 'root',
|
||||||
|
children: [],
|
||||||
|
edges: [],
|
||||||
|
layoutOptions: this.defaultLayoutOptions
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -187,9 +189,6 @@ export class NetlistRender {
|
|||||||
this.renderWidth = element.clientWidth;
|
this.renderWidth = element.clientWidth;
|
||||||
this.registerResizeHandler(element);
|
this.registerResizeHandler(element);
|
||||||
|
|
||||||
// 根据 height 进行放缩(可以通过设置进行调整)
|
|
||||||
const ratio = this.renderHeight / virtualHeight;
|
|
||||||
|
|
||||||
// 遍历计算布局进行创建
|
// 遍历计算布局进行创建
|
||||||
const svg = d3.select(container)
|
const svg = d3.select(container)
|
||||||
.selectAll('svg')
|
.selectAll('svg')
|
||||||
@ -203,13 +202,14 @@ export class NetlistRender {
|
|||||||
console.log(computedLayout);
|
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
|
// svg 挂载为全局注册的 selection
|
||||||
this.selection = svg;
|
this.selection = svg;
|
||||||
|
this.g = g;
|
||||||
|
|
||||||
// 注册平移和缩放
|
// 注册平移和缩放
|
||||||
this.registerRenderTransform(g);
|
this.registerRenderTransform(g);
|
||||||
@ -229,9 +229,8 @@ export class NetlistRender {
|
|||||||
* @description 绘制实体
|
* @description 绘制实体
|
||||||
* @param {d3.Selection} parentSelection
|
* @param {d3.Selection} parentSelection
|
||||||
* @param {ElkNode} computedLayout
|
* @param {ElkNode} computedLayout
|
||||||
* @param {number} ratio
|
|
||||||
*/
|
*/
|
||||||
async renderEntity(parentSelection, computedLayout, ratio) {
|
async renderEntity(parentSelection, computedLayout) {
|
||||||
// node 可能是如下的几类
|
// node 可能是如下的几类
|
||||||
// - module 的 port
|
// - module 的 port
|
||||||
// - 器件(基础器件 & 例化模块)
|
// - 器件(基础器件 & 例化模块)
|
||||||
@ -251,7 +250,8 @@ export class NetlistRender {
|
|||||||
const skin = skinManager.querySkin(node.renderName);
|
const skin = skinManager.querySkin(node.renderName);
|
||||||
if (skin) {
|
if (skin) {
|
||||||
// 具有 skin 的器件
|
// 具有 skin 的器件
|
||||||
this.cellRender.addAsD3DataItem(node, skin.meta.svgDoc.documentElement);
|
const element = skin.meta.svgDoc.documentElement.cloneNode(true);
|
||||||
|
this.cellRender.addAsD3DataItem(node, element);
|
||||||
} else {
|
} else {
|
||||||
if (node.renderType === 'port') {
|
if (node.renderType === 'port') {
|
||||||
this.portRender.addAsD3DataItem(node);
|
this.portRender.addAsD3DataItem(node);
|
||||||
@ -267,19 +267,20 @@ export class NetlistRender {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.portRender.render();
|
const ports = this.portRender.render();
|
||||||
this.instantiationRender.render();
|
const instances = this.instantiationRender.render();
|
||||||
this.cellRender.render();
|
const cells = this.cellRender.render();
|
||||||
this.connectionRender.render();
|
const connections = this.connectionRender.render();
|
||||||
|
|
||||||
|
return { ports, instances, cells, connections };
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description 绘制连线
|
* @description 绘制连线
|
||||||
* @param {d3.Selection} parentSelection
|
* @param {d3.Selection} parentSelection
|
||||||
* @param {ElkNode} computedLayout
|
* @param {ElkNode} computedLayout
|
||||||
* @param {number} ratio
|
|
||||||
*/
|
*/
|
||||||
async renderLine(parentSelection, computedLayout, ratio) {
|
async renderLine(parentSelection, computedLayout) {
|
||||||
this.wireRender = new WireRender(parentSelection, this);
|
this.wireRender = new WireRender(parentSelection, this);
|
||||||
|
|
||||||
for (const edge of computedLayout.edges) {
|
for (const edge of computedLayout.edges) {
|
||||||
@ -455,16 +456,167 @@ export class NetlistRender {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @description 展开例化模块
|
* @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 关闭例化模块
|
* @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,
|
portnames,
|
||||||
rx: 3,
|
rx: 3,
|
||||||
ry: 3,
|
ry: 3,
|
||||||
expandText: '📌'
|
expandText: '📌',
|
||||||
|
_self: node
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -114,7 +115,10 @@ export class InstantiationRender {
|
|||||||
.attr('y', LAYOUT_CONSTANT.INSTANCE_TITLE_HEIGHT)
|
.attr('y', LAYOUT_CONSTANT.INSTANCE_TITLE_HEIGHT)
|
||||||
.attr('width', data => data.width)
|
.attr('width', data => data.width)
|
||||||
.attr('height', data => data.height - LAYOUT_CONSTANT.INSTANCE_TITLE_HEIGHT)
|
.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')
|
let texts = instantiationSelections.append('text')
|
||||||
@ -138,7 +142,15 @@ export class InstantiationRender {
|
|||||||
.attr('fill', 'var(--main-color)');
|
.attr('fill', 'var(--main-color)');
|
||||||
})
|
})
|
||||||
.on('click', function(event) {
|
.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,
|
x: 0,
|
||||||
y: yOffset
|
y: yOffset
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 计算右侧的
|
// 计算右侧的
|
||||||
|
@ -45,6 +45,7 @@ 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);
|
const cell = new Cell(this, cellName, rawCell);
|
||||||
this._nameToCell.set(cellName, cell);
|
this._nameToCell.set(cellName, cell);
|
||||||
|
@ -67,6 +67,9 @@ class SkinMeta {
|
|||||||
// 颜色替换
|
// 颜色替换
|
||||||
// 填充颜色
|
// 填充颜色
|
||||||
svgString = svgString.replace(/#279BB0/g, 'var(--main-dark-color)');
|
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)');
|
svgString = svgString.replace(/#000000/g, 'var(--main-color)');
|
||||||
// 字体颜色
|
// 字体颜色
|
||||||
|
Loading…
x
Reference in New Issue
Block a user