实现取消 port 节点,和外部直连
This commit is contained in:
parent
e652631261
commit
64fdc7408b
@ -171,6 +171,8 @@ import { Module } from "./render/layout";
|
||||
* @property {number} [y] 端口的 Y 坐标(可选,由布局算法生成)。
|
||||
* @property {number} [width] 端口的宽度(可选)。
|
||||
* @property {number} [height] 端口的高度(可选)。
|
||||
* @property {'input' | 'output'} [direction] 端口在这个实体上的方向
|
||||
* @property {'port' | 'instance' | 'cell'} [source] 这是谁的端口
|
||||
* @property {ElkLayoutOptions} [layoutOptions] 端口的布局选项(可选)。
|
||||
*/
|
||||
|
||||
|
@ -53,7 +53,7 @@ export class CellRender {
|
||||
.enter()
|
||||
.append(data => {
|
||||
const element = data.element;
|
||||
element.setAttribute('x', data.x);
|
||||
element.setAttribute('x', data.x + LAYOUT_CONSTANT.CELL_LEFT_MARGIN);
|
||||
element.setAttribute('y', data.y);
|
||||
if (globalSetting.renderAnimation) {
|
||||
element.setAttribute('opacity', 0);
|
||||
|
@ -1,6 +1,7 @@
|
||||
import * as d3 from 'd3';
|
||||
import { globalSetting } from '../global';
|
||||
import { NetlistRender } from '.';
|
||||
import { getMarginParamter, LAYOUT_CONSTANT } from './layout';
|
||||
|
||||
export class ConnectionRender {
|
||||
/**
|
||||
@ -25,16 +26,19 @@ export class ConnectionRender {
|
||||
|
||||
/**
|
||||
* @description 将 elknode 关于 module connection 的数据添加为 d3 数据项目
|
||||
* @param {ElkPort} cellPort 连接点对象
|
||||
* @param {ElkNode} node 当前的实体(port/例化模块/器件)
|
||||
* @param {import('../jsdoc').ElkPort} cellPort 连接点对象
|
||||
* @param {import('../jsdoc').ElkNode} node 当前的实体(port/例化模块/器件)
|
||||
*/
|
||||
addAsD3DataItem(cellPort, node) {
|
||||
addAsD3DataItem(port, node) {
|
||||
const marginParamter = getMarginParamter(port);
|
||||
const marginOffset = port.direction === 'input' ? marginParamter.leftMargin: - marginParamter.rightMargin;
|
||||
|
||||
this.data.push({
|
||||
id: cellPort.id,
|
||||
x: cellPort.x + node.x,
|
||||
y: cellPort.y + node.y + 0.5, // 0.5 是为了线宽
|
||||
width: cellPort.width,
|
||||
height: cellPort.height,
|
||||
id: port.id,
|
||||
x: port.x + node.x + marginOffset,
|
||||
y: port.y + node.y + 0.5, // 0.5 是为了线宽
|
||||
width: port.width,
|
||||
height: port.height,
|
||||
fill: 'var(--main-color)',
|
||||
text: '',
|
||||
r: 3.5
|
||||
|
@ -288,6 +288,14 @@ export class NetlistRender {
|
||||
this.wireRender = new WireRender(parentSelection, this);
|
||||
this.crossDotRender = new CrossDotRender(parentSelection, this);
|
||||
|
||||
// 建立关于 port 的索引
|
||||
const id2port = new Map();
|
||||
for (const node of computedLayout.children || []) {
|
||||
for (const port of node.ports || []) {
|
||||
id2port.set(port.id, port);
|
||||
}
|
||||
}
|
||||
|
||||
const rangeTree = new RangeTreeMap();
|
||||
|
||||
for (const edge of computedLayout.edges) {
|
||||
@ -298,7 +306,7 @@ export class NetlistRender {
|
||||
points.push(point);
|
||||
}
|
||||
points.push(section.endPoint);
|
||||
this.wireRender.addAsD3DataItems(points, edge);
|
||||
this.wireRender.addAsD3DataItems(points, edge, id2port);
|
||||
|
||||
// 加入 range tree 中
|
||||
for (let i = 0; i < points.length - 1; ++ i) {
|
||||
@ -502,6 +510,7 @@ export class NetlistRender {
|
||||
const originX = elkNode.x;
|
||||
const originY = elkNode.y;
|
||||
|
||||
// 给所有的子图的节点增加前缀名
|
||||
function addNodeIdPrefix(nodes) {
|
||||
for (const node of nodes) {
|
||||
node.id = dotConnect(elkNode.name, node.id);
|
||||
@ -511,6 +520,7 @@ export class NetlistRender {
|
||||
}
|
||||
}
|
||||
|
||||
// 给所有的子图的边增加前缀名
|
||||
function addEdgeIdPrefix(edges) {
|
||||
for (const edge of edges) {
|
||||
edge.id = dotConnect(elkNode.name, edge.id);
|
||||
@ -531,11 +541,41 @@ export class NetlistRender {
|
||||
addNodeIdPrefix(constantNodes);
|
||||
addEdgeIdPrefix(connectionEdges);
|
||||
|
||||
// 对于子图而言,不需要创建 ports,只需要把对应的连线接到所在例化模块的接口即可.
|
||||
// 只需要对当前 node 中的 port 进行 index,然后对于 connectionEdges 中对应的部分的 id 进行替换即可
|
||||
const portName2port = new Map();
|
||||
for (const port of elkNode.ports || []) {
|
||||
portName2port.set(port.renderName, port);
|
||||
}
|
||||
|
||||
const innerPortId2outterPortId = new Map();
|
||||
for (const innerPort of portNodes) {
|
||||
const outterPort = portName2port.get(innerPort.name);
|
||||
if (outterPort) {
|
||||
innerPortId2outterPortId.set(innerPort.id, outterPort.id);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
for (const edge of connectionEdges) {
|
||||
if (innerPortId2outterPortId.has(edge.source)) {
|
||||
const outterId = innerPortId2outterPortId.get(edge.source);
|
||||
edge.source = elkNode.id;
|
||||
edge.sourcePort = outterId;
|
||||
}
|
||||
|
||||
if (innerPortId2outterPortId.has(edge.target)) {
|
||||
const outterId = innerPortId2outterPortId.get(edge.target);
|
||||
edge.target = elkNode.id;
|
||||
edge.targetPort = outterId;
|
||||
}
|
||||
}
|
||||
|
||||
// 加入到布局图中
|
||||
elkNode.children = [];
|
||||
elkNode.edges = [];
|
||||
elkNode.layoutOptions = this.defaultLayoutOptions;
|
||||
|
||||
elkNode.children.push(...portNodes);
|
||||
elkNode.children.push(...cellNodes);
|
||||
elkNode.children.push(...constantNodes);
|
||||
elkNode.edges.push(...connectionEdges);
|
||||
@ -575,8 +615,11 @@ export class NetlistRender {
|
||||
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);
|
||||
|
@ -40,7 +40,7 @@ export class InstantiationRender {
|
||||
for (const port of node.ports || []) {
|
||||
const isInput = port.x < LAYOUT_CONSTANT.INSTANTIATION_WIDTH;
|
||||
const align = isInput ? 'left': 'end';
|
||||
const portX = isInput ? port.x + textPadding : port.x - textPadding;
|
||||
const portX = isInput ? port.x + textPadding : port.x - textPadding - LAYOUT_CONSTANT.INSTANCE_LEFT_MARGIN - LAYOUT_CONSTANT.INSTANCE_RIGHT_MARGIN;
|
||||
portnames.push({
|
||||
name: port.renderName,
|
||||
x: portX,
|
||||
@ -82,14 +82,15 @@ export class InstantiationRender {
|
||||
|
||||
// 例化模块的方块
|
||||
let instances = instantiationSelections.append('rect')
|
||||
.attr('x', LAYOUT_CONSTANT.INSTANCE_LEFT_MARGIN)
|
||||
.attr('y', LAYOUT_CONSTANT.INSTANCE_TITLE_HEIGHT)
|
||||
.attr('width', data => data.width)
|
||||
.attr('width', data => data.width - LAYOUT_CONSTANT.INSTANCE_LEFT_MARGIN - LAYOUT_CONSTANT.INSTANCE_RIGHT_MARGIN)
|
||||
.attr('height', data => data.height - LAYOUT_CONSTANT.INSTANCE_TITLE_HEIGHT);
|
||||
|
||||
|
||||
// 说明文字
|
||||
let texts = instantiationSelections.append('text')
|
||||
.attr('x', 0)
|
||||
.attr('x', LAYOUT_CONSTANT.INSTANCE_LEFT_MARGIN)
|
||||
.attr('y', LAYOUT_CONSTANT.INSTANCE_TITLE_HEIGHT - 8)
|
||||
.attr('dominant-baseline', 'middle')
|
||||
.attr('text-anchor', 'left')
|
||||
@ -113,7 +114,7 @@ export class InstantiationRender {
|
||||
.enter()
|
||||
.append('text')
|
||||
.attr('class', 'port-name')
|
||||
.attr('x', d => d.x)
|
||||
.attr('x', d => d.x + LAYOUT_CONSTANT.INSTANCE_LEFT_MARGIN)
|
||||
.attr('y', d => d.y)
|
||||
.attr('dominant-baseline', 'middle')
|
||||
.attr('text-anchor', d => d.align)
|
||||
@ -125,7 +126,7 @@ export class InstantiationRender {
|
||||
|
||||
// 增加一个背景方块,防止 svg 点不到
|
||||
let bgFullScreenSelections = instantiationSelections.append('rect')
|
||||
.attr('x', 5)
|
||||
.attr('x', 5 + LAYOUT_CONSTANT.INSTANCE_LEFT_MARGIN)
|
||||
.attr('y', LAYOUT_CONSTANT.INSTANCE_TITLE_HEIGHT + 5)
|
||||
.attr('width', 20)
|
||||
.attr('height', 20)
|
||||
@ -149,7 +150,7 @@ export class InstantiationRender {
|
||||
let fullSreenSelections = instantiationSelections.append(data => {
|
||||
// 获取全屏图标
|
||||
const element = svgResource.get('full-screen');
|
||||
element.setAttribute('x', 5);
|
||||
element.setAttribute('x', 5 + LAYOUT_CONSTANT.INSTANCE_LEFT_MARGIN);
|
||||
element.setAttribute('y', LAYOUT_CONSTANT.INSTANCE_TITLE_HEIGHT + 5);
|
||||
element.setAttribute('width', 20);
|
||||
element.setAttribute('height', 20);
|
||||
|
@ -99,6 +99,9 @@ export class Module {
|
||||
const portConnection = {
|
||||
id: dotConnect(port.id, '0'),
|
||||
renderType: 'portConnection',
|
||||
source: 'port',
|
||||
// 对于 input 的 port 而言,它的端口只有一个且为输出
|
||||
direction: 'output',
|
||||
width: LAYOUT_CONSTANT.CELL_PORT_WIDTH,
|
||||
height: LAYOUT_CONSTANT.CELL_PORT_HEIGHT,
|
||||
x: LAYOUT_CONSTANT.PORT_WIDTH,
|
||||
@ -110,6 +113,9 @@ export class Module {
|
||||
name: port.name,
|
||||
renderName: name,
|
||||
renderType: 'port',
|
||||
source: 'port',
|
||||
// 对于 input 的 port 而言,它的端口只有一个且为输入
|
||||
direction: 'input',
|
||||
type: port.direction,
|
||||
width: LAYOUT_CONSTANT.PORT_WIDTH,
|
||||
height: LAYOUT_CONSTANT.PORT_HEIGHT,
|
||||
@ -312,7 +318,7 @@ export class Module {
|
||||
const meta = skin.meta;
|
||||
|
||||
const height = meta.height;
|
||||
const width = meta.width;
|
||||
const width = meta.width + LAYOUT_CONSTANT.CELL_LEFT_MARGIN + LAYOUT_CONSTANT.CELL_RIGHT_MARGIN;
|
||||
|
||||
const ports = [];
|
||||
|
||||
@ -337,6 +343,8 @@ export class Module {
|
||||
id: connection.id,
|
||||
renderName: connection.name,
|
||||
renderType: 'cellPort',
|
||||
direction: 'input',
|
||||
source: 'cell',
|
||||
width: LAYOUT_CONSTANT.CELL_PORT_WIDTH,
|
||||
height: LAYOUT_CONSTANT.CELL_PORT_HEIGHT,
|
||||
x: 0,
|
||||
@ -353,6 +361,8 @@ export class Module {
|
||||
id: connection.id,
|
||||
renderName: connection.name,
|
||||
renderType: 'cellPort',
|
||||
direction: 'output',
|
||||
source: 'cell',
|
||||
width: LAYOUT_CONSTANT.CELL_PORT_WIDTH,
|
||||
height: LAYOUT_CONSTANT.CELL_PORT_HEIGHT,
|
||||
x: width,
|
||||
@ -425,6 +435,8 @@ export class Module {
|
||||
id: connection.id,
|
||||
renderName: connectionName,
|
||||
renderType: 'cellPort',
|
||||
direction: 'input',
|
||||
source: 'instance',
|
||||
width: LAYOUT_CONSTANT.CELL_PORT_WIDTH,
|
||||
height: LAYOUT_CONSTANT.CELL_PORT_HEIGHT,
|
||||
x: 0,
|
||||
@ -440,6 +452,8 @@ export class Module {
|
||||
id: connection.id,
|
||||
renderName: connectionName,
|
||||
renderType: 'cellPort',
|
||||
direction: 'output',
|
||||
source: 'instance',
|
||||
width: LAYOUT_CONSTANT.CELL_PORT_WIDTH,
|
||||
height: LAYOUT_CONSTANT.CELL_PORT_HEIGHT,
|
||||
x: instanceWidth,
|
||||
@ -469,3 +483,42 @@ export class Module {
|
||||
return node;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {import('../jsdoc').ElkPort} port
|
||||
* @returns {ConnectionMarginParameter}
|
||||
*/
|
||||
export function getMarginParamter(port) {
|
||||
if (!port) {
|
||||
// 如果是例化模块,那么当前的 port 就是空
|
||||
// 因为例化模块内部的变量一定和外部直连,所以例化模块内部是没有 port 的
|
||||
return {
|
||||
leftMargin: 0,
|
||||
rightMargin: 0
|
||||
}
|
||||
}
|
||||
switch (port.source) {
|
||||
case 'instance':
|
||||
return {
|
||||
leftMargin: LAYOUT_CONSTANT.INSTANCE_LEFT_MARGIN,
|
||||
rightMargin: LAYOUT_CONSTANT.INSTANCE_RIGHT_MARGIN
|
||||
}
|
||||
case 'cell':
|
||||
return {
|
||||
leftMargin: LAYOUT_CONSTANT.CELL_LEFT_MARGIN,
|
||||
rightMargin: LAYOUT_CONSTANT.CELL_RIGHT_MARGIN
|
||||
}
|
||||
default:
|
||||
return {
|
||||
leftMargin: 0,
|
||||
rightMargin: 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef ConnectionMarginParameter
|
||||
* @property {number} leftMargin
|
||||
* @property {number} rightMargin
|
||||
*/
|
@ -3,7 +3,7 @@
|
||||
import * as d3 from 'd3';
|
||||
import { globalSetting } from '../global';
|
||||
import { NetlistRender } from '.';
|
||||
import { LAYOUT_CONSTANT, LINE_WIDTH } from './layout';
|
||||
import { getMarginParamter, LAYOUT_CONSTANT, LINE_WIDTH } from './layout';
|
||||
import { svgResource } from '../skin/draw';
|
||||
import { PulseLine } from '../skin/plusation';
|
||||
|
||||
@ -36,11 +36,24 @@ export class WireRender {
|
||||
|
||||
/**
|
||||
* @description 将 elknode 关于 wire 的数据添加为 d3 数据项目
|
||||
* @param {ElkPoint[]} points
|
||||
* @param {ElkEdge} edge
|
||||
* @param {import('../jsdoc').ElkPoint[]} points 长度至少为 2 的数组,代表经历过的点集
|
||||
* @param {import('../jsdoc').ElkEdge} edge
|
||||
* @param {Map<string, import('../jsdoc').ElkPort>} id2port
|
||||
*/
|
||||
addAsD3DataItems(points, edge) {
|
||||
addAsD3DataItems(points, edge, id2port) {
|
||||
const linePaths = [];
|
||||
|
||||
const beginPoint = points.at(0);
|
||||
const endPoint = points.at(-1);
|
||||
|
||||
const sourcePort = id2port.get(edge.sourcePort);
|
||||
const targetPort = id2port.get(edge.targetPort);
|
||||
const sourceMargin = getMarginParamter(sourcePort);
|
||||
const targetMargin = getMarginParamter(targetPort);
|
||||
|
||||
beginPoint.x -= sourceMargin.rightMargin;
|
||||
endPoint.x += targetMargin.leftMargin;
|
||||
|
||||
for (let i = 0; i < points.length; ++ i) {
|
||||
// 根据点的信息创建 path
|
||||
const command = i === 0 ? 'M': 'L';
|
||||
@ -52,7 +65,6 @@ export class WireRender {
|
||||
const lineSvg = linePaths.join(' ');
|
||||
|
||||
// 判断当前的朝向
|
||||
const endPoint = points.at(-1);
|
||||
const direction = endPoint.x > points.at(-2).x ? 'right' : 'left';
|
||||
const arrowX = direction === 'right' ? endPoint.x - LAYOUT_CONSTANT.CELL_PORT_WIDTH - this.arrowWidth + 2.5:
|
||||
endPoint.x + LAYOUT_CONSTANT.CELL_PORT_WIDTH - 2.5
|
||||
|
Loading…
x
Reference in New Issue
Block a user