diff --git a/src/hook/jsdoc.js b/src/hook/jsdoc.js index e881a88..4da3210 100644 --- a/src/hook/jsdoc.js +++ b/src/hook/jsdoc.js @@ -185,6 +185,8 @@ import { Module } from "./render/layout"; * @property {string[]} targets 边的目标节点列表。 * @property {string} target 边的目标节点。 * @property {string} targetPort 边的目标节点的port。 + * @property {string} [orginalSource] + * @property {string} [orginalTarget] * @property {string} [container] 所在容器的 id (可选,由布局算法生成) * @property {ElkSection[]} [sections] 具体的连线规则(可选,由布局算法生成) * @property {ElkLayoutOptions} [layoutOptions] 边的布局选项(可选)。 diff --git a/src/hook/render/index.js b/src/hook/render/index.js index 98568c7..fb9cc13 100644 --- a/src/hook/render/index.js +++ b/src/hook/render/index.js @@ -232,7 +232,7 @@ export class NetlistRender { /** * @description 绘制实体 * @param {d3.Selection} parentSelection - * @param {ElkNode} computedLayout + * @param {import('../jsdoc').ElkNode} computedLayout */ async renderEntity(parentSelection, computedLayout) { // node 可能是如下的几类 @@ -560,12 +560,14 @@ export class NetlistRender { for (const edge of connectionEdges) { if (innerPortId2outterPortId.has(edge.source)) { const outterId = innerPortId2outterPortId.get(edge.source); + edge.orginalSource = edge.source; edge.source = elkNode.id; edge.sourcePort = outterId; } if (innerPortId2outterPortId.has(edge.target)) { const outterId = innerPortId2outterPortId.get(edge.target); + edge.orginalTarget = edge.target; edge.target = elkNode.id; edge.targetPort = outterId; } @@ -576,6 +578,8 @@ export class NetlistRender { elkNode.edges = []; elkNode.layoutOptions = this.defaultLayoutOptions; + + // elkNode.children.push(...portNodes); elkNode.children.push(...cellNodes); elkNode.children.push(...constantNodes); elkNode.edges.push(...connectionEdges); @@ -617,7 +621,6 @@ export class NetlistRender { const layout = s.layout; await this.renderLine(parentSelection, layout); - const { instances } = await this.renderEntity(parentSelection, layout); const id2selection = new Map(); diff --git a/src/hook/render/instantiation.js b/src/hook/render/instantiation.js index 8f53381..20de7ce 100644 --- a/src/hook/render/instantiation.js +++ b/src/hook/render/instantiation.js @@ -28,26 +28,32 @@ export class InstantiationRender { /** * @description 将 elknode 关于 例化模块 的数据添加为 d3 数据项目 - * @param {ElkNode} node + * @param {import('../jsdoc').ElkNode} node */ addAsD3DataItem(node) { const nodeModule = this.rootRender.nameToModule.get(node.renderName); const view = nodeModule.view; const textPadding = 5; - const portnames = []; + // 当前的例化是否为一个展开的例化 + this.isExpanded = (node.children || []).length > 0; + const topPadding = this.isExpanded ? 25 : 0; + 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 - LAYOUT_CONSTANT.INSTANCE_LEFT_MARGIN - LAYOUT_CONSTANT.INSTANCE_RIGHT_MARGIN; + const portY = this.isExpanded ? port.y - 10: port.y; + portnames.push({ name: port.renderName, x: portX, - y: port.y, + y: portY, align }); } + this.data.push({ id: node.id, @@ -56,7 +62,8 @@ export class InstantiationRender { y: node.y, name: node.name, width: node.width, - height: node.height, + height: node.height + topPadding, + topPadding, text: node.renderName, portnames, rx: 3, @@ -83,7 +90,7 @@ export class InstantiationRender { // 例化模块的方块 let instances = instantiationSelections.append('rect') .attr('x', LAYOUT_CONSTANT.INSTANCE_LEFT_MARGIN) - .attr('y', LAYOUT_CONSTANT.INSTANCE_TITLE_HEIGHT) + .attr('y', d => LAYOUT_CONSTANT.INSTANCE_TITLE_HEIGHT - d.topPadding) .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); @@ -91,7 +98,7 @@ export class InstantiationRender { // 说明文字 let texts = instantiationSelections.append('text') .attr('x', LAYOUT_CONSTANT.INSTANCE_LEFT_MARGIN) - .attr('y', LAYOUT_CONSTANT.INSTANCE_TITLE_HEIGHT - 8) + .attr('y', d => LAYOUT_CONSTANT.INSTANCE_TITLE_HEIGHT - 8 - d.topPadding) .attr('dominant-baseline', 'middle') .attr('text-anchor', 'left') .attr('fill', 'var(--foreground)') @@ -127,7 +134,7 @@ export class InstantiationRender { // 增加一个背景方块,防止 svg 点不到 let bgFullScreenSelections = instantiationSelections.append('rect') .attr('x', 5 + LAYOUT_CONSTANT.INSTANCE_LEFT_MARGIN) - .attr('y', LAYOUT_CONSTANT.INSTANCE_TITLE_HEIGHT + 5) + .attr('y', d => LAYOUT_CONSTANT.INSTANCE_TITLE_HEIGHT + 5 - d.topPadding) .attr('width', 20) .attr('height', 20) .attr('opacity', 0) @@ -151,7 +158,7 @@ export class InstantiationRender { // 获取全屏图标 const element = svgResource.get('full-screen'); element.setAttribute('x', 5 + LAYOUT_CONSTANT.INSTANCE_LEFT_MARGIN); - element.setAttribute('y', LAYOUT_CONSTANT.INSTANCE_TITLE_HEIGHT + 5); + element.setAttribute('y', LAYOUT_CONSTANT.INSTANCE_TITLE_HEIGHT + 5 - data.topPadding); element.setAttribute('width', 20); element.setAttribute('height', 20); element.setAttribute('class', 'pointer'); diff --git a/src/hook/render/wire.js b/src/hook/render/wire.js index 941d7e5..7220b44 100644 --- a/src/hook/render/wire.js +++ b/src/hook/render/wire.js @@ -50,9 +50,17 @@ export class WireRender { const targetPort = id2port.get(edge.targetPort); const sourceMargin = getMarginParamter(sourcePort); const targetMargin = getMarginParamter(targetPort); - + beginPoint.x -= sourceMargin.rightMargin; - endPoint.x += targetMargin.leftMargin; + + // 特殊情况: targetPort 为 undefined + if (targetPort === undefined) { + const port = id2port.get(edge.orginalTarget); + // TODO: 检查正确性 + endPoint.x -= LAYOUT_CONSTANT.INSTANCE_RIGHT_MARGIN; + } else { + endPoint.x += targetMargin.leftMargin; + } for (let i = 0; i < points.length; ++ i) { // 根据点的信息创建 path