2024-12-31 21:13:04 +08:00

274 lines
9.5 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import * as d3 from 'd3';
import { NetlistRender } from '.';
import { globalSetting } from '../global';
import { LAYOUT_CONSTANT } from './layout';
import { svgResource } from '../skin/draw';
export class InstantiationRender {
/**
*
* @param {d3.Selection} selection
* @param {NetlistRender} rootRender
*/
constructor(selection, rootRender) {
this.parentSelection = selection;
this.rootRender = rootRender;
/**
* @type {BasicD3DataItem[]}
*/
this.data = []
/**
* @description id 到管理数据项的映射
* @type {Map<string, BasicD3ManagmentItem[]>}
*/
this.id2manager = rootRender.id2manager;
}
/**
* @description 将 elknode 关于 例化模块 的数据添加为 d3 数据项目
* @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: portY,
align
});
}
this.data.push({
id: node.id,
type: node.renderName, // 例化模块的模块名字
x: node.x,
y: node.y,
name: node.name,
width: node.width,
height: node.height + topPadding,
topPadding,
text: node.renderName,
portnames,
rx: 3,
ry: 3,
// 数据本身
_self: node
});
}
render() {
const data = this.data;
const rootRender = this.rootRender;
const id2manager = this.id2manager;
const _this = this;
// 外部控制主体的 g
let instantiationSelections = this.parentSelection.selectAll('g.instance')
.data(data)
.enter()
.append('g')
.attr('class', 'instance')
.attr("transform", d => `translate(${d.x}, ${d.y})`);
// 例化模块的方块
let instances = instantiationSelections.append('rect')
.attr('x', LAYOUT_CONSTANT.INSTANCE_LEFT_MARGIN)
.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);
// 说明文字
let texts = instantiationSelections.append('text')
.attr('x', LAYOUT_CONSTANT.INSTANCE_LEFT_MARGIN)
.attr('y', d => LAYOUT_CONSTANT.INSTANCE_TITLE_HEIGHT - 8 - d.topPadding)
.attr('dominant-baseline', 'middle')
.attr('text-anchor', 'left')
.attr('fill', 'var(--foreground)')
.attr('font-size', '14px')
.attr('class', 'port-caption')
.text(data => data.name)
.each(function(data) {
const text = d3.select(this);
const bbox = text.node().getBBox();
data.textX = (data.width - bbox.width) / 2;
})
.attr('x', d => d.textX)
// 渲染 portnames
instantiationSelections.each(function(node) {
d3.select(this)
.selectAll('.port-name')
.data(node.portnames)
.enter()
.append('text')
.attr('class', 'port-name')
.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)
.attr('fill', 'var(--foreground)')
.attr('font-size', '11px')
.text(d => d.name);
});
// 增加一个背景方块,防止 svg 点不到
let bgFullScreenSelections = instantiationSelections.append('rect')
.attr('x', 5 + LAYOUT_CONSTANT.INSTANCE_LEFT_MARGIN)
.attr('y', d => LAYOUT_CONSTANT.INSTANCE_TITLE_HEIGHT + 5 - d.topPadding)
.attr('width', 20)
.attr('height', 20)
.attr('opacity', 0)
.attr('class', 'pointer')
.on('click', function(data) {
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);
}
});
const id2element = new Map();
// 渲染左上角的放大缩小图标,并注册对应的展开/收起事件
let fullSreenSelections = instantiationSelections.append(data => {
// 获取全屏图标
const element = svgResource.get('full-screen');
element.setAttribute('x', 5 + LAYOUT_CONSTANT.INSTANCE_LEFT_MARGIN);
element.setAttribute('y', LAYOUT_CONSTANT.INSTANCE_TITLE_HEIGHT + 5 - data.topPadding);
element.setAttribute('width', 20);
element.setAttribute('height', 20);
element.setAttribute('class', 'pointer');
return element;
})
.each(function(data) {
const svgElement = d3.select(this);
id2element.set(data.id, svgElement);
})
.on('mouseenter', function() {
const svgElement = d3.select(this);
svgElement.selectAll("*")
.attr("fill", "var(--instance-color)");
})
.on('mouseleave', function() {
const svgElement = d3.select(this);
svgElement.selectAll("*")
.attr("fill", "var(--foreground)");
})
.on('click', function(data) {
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);
}
});
bgFullScreenSelections
.on('mouseenter', function(_, data) {
const svgElement = id2element.get(data.id);
svgElement.selectAll("*")
.attr("fill", "var(--instance-color)");
})
.on('mouseleave', function(_, data) {
const svgElement = id2element.get(data.id);
svgElement.selectAll("*")
.attr("fill", "var(--foreground)");
});
if (globalSetting.renderAnimation) {
instances
.transition()
.duration(1000)
.attr('fill', d => {
const children = d._self.children || [];
return children.length > 0 ? 'rgba(203, 129, 218, 0.1)' : 'var(--instance-fill-color)';
})
.attr('stroke', 'var(--instance-color)')
.attr('stroke-width', 2);
} else {
instances
.attr('fill', d => {
const children = d._self.children || [];
return children.length > 0 ? 'rgba(203, 129, 218, 0.1)' : 'var(--instance-fill-color)';
})
.attr('stroke', 'var(--instance-color)')
.attr('stroke-width', 2)
}
instantiationSelections
.attr('class', 'grab')
.each(function (data) {
const portSelection = d3.select(this);
// const manager = _this.createDataManager(portSelection, data);
// TODO: 实现拖拽
// registerDragEvent(manager, rootRender);
});
this.selections = instantiationSelections;
return instantiationSelections;
}
/**
*
* @param {d3.Selection} selection
* @param {BasicD3DataItem} data
* @returns {BasicD3ManagmentItem}
*/
createDataManager(selection, data) {
const id2manager = this.id2manager;
// 创建拖拽上下文
const dragContext = {
neighbors: [],
elkGraph: {
// elk 是无状态的id 取什么名字都行
id: 'root',
children: [],
edges: [],
layoutOptions: {}
}
}
const managerItem = {
data,
selection,
type: 'cell',
dragContext
};
if (!id2manager.has(data.id)) {
id2manager.set(data.id, []);
}
id2manager.get(data.id).push(managerItem);
return managerItem;
}
}