实现动态增加线宽

This commit is contained in:
锦恢 2025-01-02 07:26:36 +08:00
parent 7a6ed915d0
commit 0b8e986990
5 changed files with 101 additions and 67 deletions

View File

@ -15,7 +15,7 @@
<script> <script>
window.readNetFile = async () => { window.readNetFile = async () => {
const inputVcdFile = 'full_adder.json'; const inputVcdFile = 'IF_ID.json';
const skin = 'dide.skin'; const skin = 'dide.skin';
const r1 = await fetch(inputVcdFile); const r1 = await fetch(inputVcdFile);
const r2 = await fetch(skin); const r2 = await fetch(skin);
@ -23,7 +23,7 @@
const skinBinary = await r2.arrayBuffer(); const skinBinary = await r2.arrayBuffer();
return [ netJson, skinBinary ]; return [ netJson, skinBinary ];
} }
window.moduleName = 'full_adder'; window.moduleName = 'IF_ID';
// window.avoidWasm = 'avoid.wasm'; // window.avoidWasm = 'avoid.wasm';
</script> </script>
</head> </head>

View File

@ -75,6 +75,7 @@
{{ t('bold-multi-width-wire') }} {{ t('bold-multi-width-wire') }}
</span> </span>
<el-switch <el-switch
@change="onBoldMultiWidthWireChange"
v-model="globalSetting.boldMultiWidthWire" v-model="globalSetting.boldMultiWidthWire"
active-text="ON" active-text="ON"
inactive-text="OFF" inactive-text="OFF"
@ -166,12 +167,13 @@
</template> </template>
<script setup> <script setup>
import { globalSetting } from '@/hook/global'; import { globalLookup, globalSetting } from '@/hook/global';
import { reactive, defineComponent, watch, ref, onMounted } from 'vue'; import { reactive, defineComponent, watch, ref, onMounted } from 'vue';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import { languageSetting } from './language'; import { languageSetting } from './language';
import { crossDotStyle, onConnectStyleChange } from './cross-dot-style'; import { crossDotStyle, onConnectStyleChange } from './cross-dot-style';
import { colorManager, onCellColorChange, onGeneralColorChange, predefinedColors } from './color'; import { colorManager, onCellColorChange, onGeneralColorChange, predefinedColors } from './color';
import { LINE_WIDTH } from '@/hook/render/layout';
defineComponent({ name: "dide-setting" }); defineComponent({ name: "dide-setting" });
const { t, locale } = useI18n(); const { t, locale } = useI18n();
@ -199,7 +201,6 @@ function onlanguagechange(code) {
} }
function onRenderArrowChange() { function onRenderArrowChange() {
const rootStyles = getComputedStyle(document.documentElement);
if (globalSetting.renderArrow) { if (globalSetting.renderArrow) {
document.documentElement.style.setProperty(`--line-arrow-opacity`, '1'); document.documentElement.style.setProperty(`--line-arrow-opacity`, '1');
} else { } else {
@ -207,6 +208,26 @@ function onRenderArrowChange() {
} }
} }
function onBoldMultiWidthWireChange() {
const netlist = globalLookup.netlistRender;
const renderView = netlist.renderView;
const stack = [renderView];
while (stack.length > 0) {
const view = stack.pop();
view.wireRender.lineSelections
.attr('stroke-width', d => {
console.log('enter');
const incrementWidth = globalSetting.boldMultiWidthWire ? 2 : 0;
return d.width > 1 ? LINE_WIDTH + incrementWidth: LINE_WIDTH;
});
for (const id of view.id2children.keys()) {
const subView = view.getChild(id);
stack.push(subView);
}
}
}
onMounted(() => { onMounted(() => {
colorManager.initColor(); colorManager.initColor();
}); });

View File

@ -15,6 +15,7 @@ import { dotConnect } from './yosys';
import { CrossDotRender } from './cross-dot'; import { CrossDotRender } from './cross-dot';
import { RangeTreeMap } from '../algorithm/range-tree'; import { RangeTreeMap } from '../algorithm/range-tree';
import { ConstantRender } from './constant'; import { ConstantRender } from './constant';
import { RenderViewNode } from './render-view';
export class NetlistRender { export class NetlistRender {
/** /**
@ -209,15 +210,16 @@ export class NetlistRender {
// 底层模块 // 底层模块
const topModule = this.nameToModule.get(this.topModuleName); const topModule = this.nameToModule.get(this.topModuleName);
// 生成连接
await this.renderLine(g, computedLayout, topModule);
// 生成实体
await this.renderEntity(g, computedLayout, topModule);
// svg 挂载为全局注册的 selection // svg 挂载为全局注册的 selection
this.selection = svg; this.selection = svg;
this.g = g;
this.renderView = new RenderViewNode(g);
// 生成连接
await this.renderLine(this.renderView, computedLayout, topModule);
// 生成实体
await this.renderEntity(this.renderView, computedLayout, topModule);
// 注册平移和缩放 // 注册平移和缩放
this.registerRenderTransform(g); this.registerRenderTransform(g);
@ -235,10 +237,10 @@ export class NetlistRender {
/** /**
* @description 绘制实体 * @description 绘制实体
* @param {d3.Selection} parentSelection * @param {RenderViewNode} view
* @param {import('../jsdoc').ElkNode} computedLayout * @param {import('../jsdoc').ElkNode} computedLayout
*/ */
async renderEntity(parentSelection, computedLayout) { async renderEntity(view, computedLayout) {
// node 可能是如下的几类 // node 可能是如下的几类
// - module 的 port // - module 的 port
// - 器件(基础器件 & 例化模块) // - 器件(基础器件 & 例化模块)
@ -246,11 +248,11 @@ export class NetlistRender {
const skinManager = globalLookup.skinManager; const skinManager = globalLookup.skinManager;
// 创建各个主要实体的 render // 创建各个主要实体的 render
this.cellRender = new CellRender(parentSelection, this); view.cellRender = new CellRender(view.g, this);
this.portRender = new PortRender(parentSelection, this); view.portRender = new PortRender(view.g, this);
this.instantiationRender = new InstantiationRender(parentSelection, this); view.instantiationRender = new InstantiationRender(view.g, this);
this.connectionRender = new ConnectionRender(parentSelection, this); view.connectionRender = new ConnectionRender(view.g, this);
this.constantRender = new ConstantRender(parentSelection, this); view.constantRender = new ConstantRender(view.g, this);
for (const node of computedLayout.children) { for (const node of computedLayout.children) {
// 只计算形体的,因为 连接点 非常小,几乎不影响布局 // 只计算形体的,因为 连接点 非常小,几乎不影响布局
@ -260,42 +262,40 @@ export class NetlistRender {
if (skin) { if (skin) {
// 具有 skin 的器件 // 具有 skin 的器件
const element = skin.meta.svgDoc.documentElement.cloneNode(true); const element = skin.meta.svgDoc.documentElement.cloneNode(true);
this.cellRender.addAsD3DataItem(node, element); view.cellRender.addAsD3DataItem(node, element);
} else { } else {
if (node.renderType === 'port') { if (node.renderType === 'port') {
this.portRender.addAsD3DataItem(node); view.portRender.addAsD3DataItem(node);
} else if (node.renderType === 'instance') { } else if (node.renderType === 'instance') {
// 没有 skin 的器件或者端口 // 没有 skin 的器件或者端口
this.instantiationRender.addAsD3DataItem(node); view.instantiationRender.addAsD3DataItem(node);
} else if (node.renderType === 'constant') { } else if (node.renderType === 'constant') {
this.constantRender.addAsD3DataItem(node); view.constantRender.addAsD3DataItem(node);
} }
} }
// 如果存在 port绘制 port // 如果存在 port绘制 port
for (const cellPort of node.ports || []) { for (const cellPort of node.ports || []) {
this.connectionRender.addAsD3DataItem(cellPort, node); view.connectionRender.addAsD3DataItem(cellPort, node);
} }
} }
const ports = this.portRender.render(); view.portRender.render();
const instances = this.instantiationRender.render(); view.instantiationRender.render();
const cells = this.cellRender.render(); view.cellRender.render();
const connections = this.connectionRender.render(); view.connectionRender.render();
const constants = this.constantRender.render(); view.constantRender.render();
return { ports, instances, cells, connections };
} }
/** /**
* @description 绘制连线和交叉点 * @description 绘制连线和交叉点
* @param {d3.Selection} parentSelection * @param {RenderViewNode} view
* @param {ElkNode} computedLayout * @param {ElkNode} computedLayout
* @param {Module} module * @param {Module} module
*/ */
async renderLine(parentSelection, computedLayout, module) { async renderLine(view, computedLayout, module) {
this.wireRender = new WireRender(parentSelection, this); view.wireRender = new WireRender(view.g, this);
this.crossDotRender = new CrossDotRender(parentSelection, this); view.crossDotRender = new CrossDotRender(view.g, this);
// 建立关于 port 的索引 // 建立关于 port 的索引
const id2port = new Map(); const id2port = new Map();
@ -316,7 +316,7 @@ export class NetlistRender {
points.push(point); points.push(point);
} }
points.push(section.endPoint); points.push(section.endPoint);
this.wireRender.addAsD3DataItems(points, edge, id2port, width); view.wireRender.addAsD3DataItems(points, edge, id2port, width);
// 加入 range tree 中 // 加入 range tree 中
for (let i = 0; i < points.length - 1; ++ i) { for (let i = 0; i < points.length - 1; ++ i) {
@ -337,14 +337,14 @@ export class NetlistRender {
for (const point of section.bendPoints || []) { for (const point of section.bendPoints || []) {
const degree = rangeTree.getDegree(point); const degree = rangeTree.getDegree(point);
if (degree >= 3) { if (degree >= 3) {
this.crossDotRender.addAsD3DataItem(point.x, point.y); view.crossDotRender.addAsD3DataItem(point.x, point.y);
} }
} }
} }
} }
this.wireRender.render(); view.wireRender.render();
this.crossDotRender.render(); view.crossDotRender.render();
} }
/** /**
@ -614,34 +614,31 @@ export class NetlistRender {
this.zoom.translateBy(this.selection, deltaX, deltaY); this.zoom.translateBy(this.selection, deltaX, deltaY);
const svg = this.selection; const svg = this.selection;
const g = this.g; const g = this.renderView.g;
g.selectAll('*').remove(); g.selectAll('*').remove();
this.renderView.id2children.clear();
// 开始递归地进行渲染 // 开始递归地进行渲染
const renderStack = [ const renderStack = [
{ {
parentSelection: g, view: this.renderView,
layout: computedLayout layout: computedLayout
} }
]; ];
while (renderStack.length > 0) { while (renderStack.length > 0) {
const s = renderStack.pop(); const s = renderStack.pop();
const parentSelection = s.parentSelection; const view = s.view;
const layout = s.layout; const layout = s.layout;
const layoutName = layout.renderName || this.topModuleName; const layoutName = layout.renderName || this.topModuleName;
const module = this.nameToModule.get(layoutName); const module = this.nameToModule.get(layoutName);
await this.renderLine(parentSelection, layout, module); await this.renderLine(view, layout, module);
const { instances } = await this.renderEntity(parentSelection, layout, module); await this.renderEntity(view, layout, module);
const id2selection = new Map(); const id2selection = view.instantiationRender.id2selection;
instances.each(function(data) {
const selection = d3.select(this);
id2selection.set(data.id, selection);
});
// 将需要渲染子图的部分扔进渲染器 // 将需要渲染子图的部分扔进渲染器
for (const node of layout.children || []) { for (const node of layout.children || []) {
@ -649,8 +646,10 @@ export class NetlistRender {
if (subChildren.length > 0) { if (subChildren.length > 0) {
const selection = id2selection.get(node.id); const selection = id2selection.get(node.id);
const subView = new RenderViewNode(selection);
view.setChild(node.id, subView);
renderStack.push({ renderStack.push({
parentSelection: selection, view: subView,
layout: node layout: node
}); });
} }
@ -702,37 +701,41 @@ export class NetlistRender {
this.zoom.translateBy(this.selection, deltaX, deltaY); this.zoom.translateBy(this.selection, deltaX, deltaY);
const svg = this.selection; const svg = this.selection;
const g = this.g; const g = this.renderView.g;
g.selectAll('*').remove(); g.selectAll('*').remove();
this.renderView.id2children.clear();
// 开始递归地进行渲染 // 开始递归地进行渲染
const renderStack = [ const renderStack = [
{ {
parentSelection: g, view: this.renderView,
layout: computedLayout layout: computedLayout
} }
]; ];
while (renderStack.length > 0) { while (renderStack.length > 0) {
const s = renderStack.pop(); const s = renderStack.pop();
const parentSelection = s.parentSelection; const view = s.view;
const layout = s.layout; const layout = s.layout;
await this.renderLine(parentSelection, layout);
const { instances } = await this.renderEntity(parentSelection, layout); const layoutName = layout.renderName || this.topModuleName;
const id2selection = new Map(); const module = this.nameToModule.get(layoutName);
instances.each(function(data) {
const selection = d3.select(this); await this.renderLine(view, layout, module);
id2selection.set(data.id, selection); await this.renderEntity(view, layout, module);
});
const id2selection = view.instantiationRender.id2selection;
// 将需要渲染子图的部分扔进渲染器 // 将需要渲染子图的部分扔进渲染器
for (const node of layout.children || []) { for (const node of layout.children || []) {
const subChildren = node.children || []; const subChildren = node.children || [];
if (subChildren.length > 0) { if (subChildren.length > 0) {
const selection = id2selection.get(node.id); const selection = id2selection.get(node.id);
const subView = new RenderViewNode(selection);
view.id2children.set(node.id, subView);
renderStack.push({ renderStack.push({
parentSelection: selection, view: subView,
layout: node layout: node
}); });
} }

View File

@ -235,6 +235,15 @@ export class InstantiationRender {
}); });
this.selections = instantiationSelections; this.selections = instantiationSelections;
const id2selection = new Map();
instantiationSelections.each(function(data) {
const selection = d3.select(this);
id2selection.set(data.id, selection);
});
this.id2selection = id2selection;
return instantiationSelections; return instantiationSelections;
} }

View File

@ -118,7 +118,6 @@ export class WireRender {
.attr("fill", "none") .attr("fill", "none")
.attr('stroke', 'var(--wire-color)') .attr('stroke', 'var(--wire-color)')
this.lineSelections = lineSelections;
const arrows = new Map(); const arrows = new Map();
let arrowSelections = this.selection.selectAll('svg.line-arrow') let arrowSelections = this.selection.selectAll('svg.line-arrow')
@ -196,21 +195,23 @@ export class WireRender {
}); });
if (globalSetting.renderAnimation) { // if (globalSetting.renderAnimation) {
lineSelections = lineSelections // lineSelections = lineSelections
.transition() // .transition()
.duration(1000); // .duration(1000);
} // }
lineSelections lineSelections
.attr('stroke-width', d => { .attr('stroke-width', d => {
const incrementWidth = globalSetting.boldMultiWidthWire ? 1 : 0; const incrementWidth = globalSetting.boldMultiWidthWire ? 2 : 0;
return d.width > 1 ? LINE_WIDTH + incrementWidth: LINE_WIDTH; return d.width > 1 ? LINE_WIDTH + incrementWidth: LINE_WIDTH;
}) })
.each(function (data) { .each(function (data) {
const selection = d3.select(this); const selection = d3.select(this);
// const manager = _this.createDataManager(selection, data); // const manager = _this.createDataManager(selection, data);
}); });
this.lineSelections = lineSelections;
} }
/** /**