实现收起功能
This commit is contained in:
parent
6587cd3150
commit
099309ade9
@ -53,7 +53,6 @@ export const colorManager = reactive({
|
||||
for (const item of this.generals) {
|
||||
const optionName = `--${item.type}-color`;
|
||||
const colorString = rootStyles.getPropertyValue(optionName);
|
||||
console.log(optionName, colorString);
|
||||
item.color = colorString;
|
||||
}
|
||||
}
|
||||
|
@ -49,7 +49,7 @@ export class CellRender {
|
||||
const _this = this;
|
||||
|
||||
let cellSelections = this.parentSelection.selectAll('svg')
|
||||
.data(data)
|
||||
.data(data, d => d.id)
|
||||
.enter()
|
||||
.append(data => {
|
||||
const element = data.element;
|
||||
|
@ -313,6 +313,8 @@ export class NetlistRender {
|
||||
this.selection.call(zoom);
|
||||
this.zoom = zoom;
|
||||
|
||||
let started = false;
|
||||
|
||||
// 缩放事件处理函数
|
||||
function zoomed(event) {
|
||||
const { transform, sourceEvent } = event;
|
||||
@ -334,11 +336,12 @@ export class NetlistRender {
|
||||
}
|
||||
} else {
|
||||
// 系统调用的变换,慢一点变换
|
||||
if (globalSetting.renderAnimation) {
|
||||
if (globalSetting.renderAnimation && !started) {
|
||||
parentSelection
|
||||
.transition()
|
||||
.duration(1000)
|
||||
.attr('transform', transform);
|
||||
started = true;
|
||||
} else {
|
||||
parentSelection.attr('transform', transform);
|
||||
}
|
||||
@ -464,6 +467,10 @@ export class NetlistRender {
|
||||
const moduleName = elkNode.renderName;
|
||||
const module = this.nameToModule.get(moduleName);
|
||||
|
||||
// 先记录当前这个选择集的位置
|
||||
const originX = elkNode.x;
|
||||
const originY = elkNode.y;
|
||||
|
||||
function addNodeIdPrefix(nodes) {
|
||||
for (const node of nodes) {
|
||||
node.id = dotConnect(elkNode.name, node.id);
|
||||
@ -512,6 +519,14 @@ export class NetlistRender {
|
||||
pinkLog(`expandInstance 布局计算耗时 ${timecost} ms`);
|
||||
console.log(computedLayout);
|
||||
|
||||
// 计算 relayout 后的当前这个例化模块的位置偏移了多少,确保点击前后的左上角坐标一致
|
||||
// 以获得最佳的用户体验
|
||||
const deltaX = originX - elkNode.x;
|
||||
const deltaY = originY - elkNode.y;
|
||||
|
||||
const transform = d3.zoomIdentity.translate(deltaX, deltaY);
|
||||
this.zoom.translateBy(this.selection, deltaX, deltaY);
|
||||
|
||||
const svg = this.selection;
|
||||
const g = this.g;
|
||||
|
||||
@ -560,15 +575,24 @@ export class NetlistRender {
|
||||
async collapseInstance(instanceSelection) {
|
||||
const data = instanceSelection.datum();
|
||||
const elkNode = data._self;
|
||||
const moduleName = elkNode.renderName;
|
||||
const module = this.nameToModule.get(moduleName);
|
||||
|
||||
// 找到所在模块
|
||||
const parentName = elkNode.parentName;
|
||||
const instanceName = elkNode.name;
|
||||
const parentModule = this.nameToModule.get(parentName);
|
||||
const cell = parentModule.view.nameToCell.get(instanceName);
|
||||
|
||||
// 先记录当前这个选择集的位置
|
||||
const originX = elkNode.x;
|
||||
const originY = elkNode.y;
|
||||
|
||||
elkNode.children = [];
|
||||
elkNode.edges = [];
|
||||
// 重新计算 elkNode 的宽度和高度
|
||||
|
||||
// 重新计算 elkNode(例化模块) 的宽度和高度
|
||||
const instanceNode = parentModule.makeInstanceNode(cell);
|
||||
for (const key of Object.keys(instanceNode)) {
|
||||
elkNode[key] = instanceNode[key];
|
||||
}
|
||||
|
||||
const start = performance.now();
|
||||
|
||||
@ -580,6 +604,12 @@ export class NetlistRender {
|
||||
pinkLog(`collapseInstance 布局计算耗时 ${timecost} ms`);
|
||||
console.log(computedLayout);
|
||||
|
||||
const deltaX = originX - elkNode.x;
|
||||
const deltaY = originY - elkNode.y;
|
||||
|
||||
const transform = d3.zoomIdentity.translate(deltaX, deltaY);
|
||||
this.zoom.translateBy(this.selection, deltaX, deltaY);
|
||||
|
||||
const svg = this.selection;
|
||||
const g = this.g;
|
||||
|
||||
@ -608,7 +638,6 @@ export class NetlistRender {
|
||||
// 将需要渲染子图的部分扔进渲染器
|
||||
for (const node of layout.children || []) {
|
||||
const subChildren = node.children || [];
|
||||
|
||||
if (subChildren.length > 0) {
|
||||
const selection = id2selection.get(node.id);
|
||||
renderStack.push({
|
||||
|
@ -62,6 +62,7 @@ export class InstantiationRender {
|
||||
portnames,
|
||||
rx: 3,
|
||||
ry: 3,
|
||||
// 数据本身
|
||||
_self: node
|
||||
});
|
||||
}
|
||||
@ -183,8 +184,6 @@ export class InstantiationRender {
|
||||
}
|
||||
});
|
||||
|
||||
console.log(id2element);
|
||||
|
||||
bgFullScreenSelections
|
||||
.on('mouseenter', function(_, data) {
|
||||
const svgElement = id2element.get(data.id);
|
||||
|
@ -168,155 +168,10 @@ export class Module {
|
||||
const skin = skinManager.querySkin(cell.type);
|
||||
|
||||
if (skin) {
|
||||
// 内置器件
|
||||
// 创建器件节点的 port, port 和 connection 一一对应
|
||||
const meta = skin.meta;
|
||||
|
||||
const height = meta.height;
|
||||
const width = meta.width;
|
||||
|
||||
const ports = [];
|
||||
|
||||
// 统计分配到左右两侧的 port
|
||||
const leftSideConnections = [];
|
||||
const rightSideConnections = [];
|
||||
|
||||
for (const conn of cell.nameToConnection.values()) {
|
||||
if (conn.direction === 'input') {
|
||||
leftSideConnections.push(conn);
|
||||
} else {
|
||||
rightSideConnections.push(conn);
|
||||
}
|
||||
}
|
||||
|
||||
// 计算左侧的
|
||||
for (let i = 0; i < leftSideConnections.length; ++ i) {
|
||||
const connection = leftSideConnections[i];
|
||||
const yOffset = meta.getPortYOffset(connection.name);
|
||||
|
||||
ports.push({
|
||||
id: connection.id,
|
||||
renderName: connection.name,
|
||||
renderType: 'cellPort',
|
||||
width: LAYOUT_CONSTANT.CELL_PORT_WIDTH,
|
||||
height: LAYOUT_CONSTANT.CELL_PORT_HEIGHT,
|
||||
x: 0,
|
||||
y: yOffset
|
||||
});
|
||||
}
|
||||
|
||||
// 计算右侧的
|
||||
for (let i = 0; i < rightSideConnections.length; ++ i) {
|
||||
const connection = rightSideConnections[i];
|
||||
const yOffset = meta.getPortYOffset(connection.name);
|
||||
|
||||
ports.push({
|
||||
id: connection.id,
|
||||
renderName: connection.name,
|
||||
renderType: 'cellPort',
|
||||
width: LAYOUT_CONSTANT.CELL_PORT_WIDTH,
|
||||
height: LAYOUT_CONSTANT.CELL_PORT_HEIGHT,
|
||||
x: width,
|
||||
y: yOffset
|
||||
});
|
||||
}
|
||||
|
||||
const node = {
|
||||
id: cell.id,
|
||||
name: cell.name,
|
||||
renderName: cell.type,
|
||||
renderType: 'cell',
|
||||
width,
|
||||
height,
|
||||
ports,
|
||||
layoutOptions: {
|
||||
'org.eclipse.elk.portConstraints': 'FIXED_POS',
|
||||
'partitioning.partition': this.commonPartitionIndex
|
||||
}
|
||||
};
|
||||
const node = this.makeCellNode(cell, skin);
|
||||
nodes.push(node);
|
||||
} else {
|
||||
// 例化模块
|
||||
|
||||
// 统计分配到左右两侧的 port
|
||||
const leftSideConnections = [];
|
||||
const rightSideConnections = [];
|
||||
|
||||
for (const conn of cell.nameToConnection.values()) {
|
||||
if (conn.direction === 'input') {
|
||||
leftSideConnections.push(conn);
|
||||
} else {
|
||||
rightSideConnections.push(conn);
|
||||
}
|
||||
}
|
||||
|
||||
// 根据一侧的 port 数量来决定 height
|
||||
const maxPortNum = Math.max(leftSideConnections.length, rightSideConnections.length);
|
||||
const instanceHeight = Math.max(
|
||||
// 这是例化模块高度的最小值
|
||||
LAYOUT_CONSTANT.INSTANTIATION_HEIGHT,
|
||||
// 每一个 connection 占据的空间为:【上方空余区域(用来防止文本的)】 + 【本身的高度】
|
||||
(LAYOUT_CONSTANT.PORT_TOP_MARGIN + LAYOUT_CONSTANT.CELL_PORT_HEIGHT) * (maxPortNum + 1)
|
||||
);
|
||||
// 宽度等于预设宽度+两倍的padding
|
||||
const instanceWidth = LAYOUT_CONSTANT.INSTANTIATION_WIDTH + 2 * LAYOUT_CONSTANT.PORT_INNER_PADDING;
|
||||
|
||||
// 创建器件节点的 port, port 和 connection 一一对应
|
||||
const ports = [];
|
||||
let inputCount = 0;
|
||||
let outputCount = 0;
|
||||
for (const connectionName of cell.nameToConnection.keys()) {
|
||||
const connection = cell.nameToConnection.get(connectionName);
|
||||
const portSide = connection.direction === 'input' ? ELK_DIRECTION.LEFT: ELK_DIRECTION.RIGHT;
|
||||
if (connection.direction === 'input') {
|
||||
inputCount ++;
|
||||
const offsetY = inputCount * LAYOUT_CONSTANT.PORT_TOP_MARGIN +
|
||||
(inputCount - 1) * LAYOUT_CONSTANT.CELL_PORT_HEIGHT +
|
||||
LAYOUT_CONSTANT.INSTANCE_TITLE_HEIGHT + LAYOUT_CONSTANT.INSTANCE_TOP_PADDING;
|
||||
|
||||
ports.push({
|
||||
id: connection.id,
|
||||
renderName: connectionName,
|
||||
renderType: 'cellPort',
|
||||
width: LAYOUT_CONSTANT.CELL_PORT_WIDTH,
|
||||
height: LAYOUT_CONSTANT.CELL_PORT_HEIGHT,
|
||||
x: 0,
|
||||
y: offsetY
|
||||
});
|
||||
} else {
|
||||
outputCount ++;
|
||||
const offsetY = outputCount * LAYOUT_CONSTANT.PORT_TOP_MARGIN +
|
||||
(outputCount - 1) * LAYOUT_CONSTANT.CELL_PORT_HEIGHT +
|
||||
LAYOUT_CONSTANT.INSTANCE_TITLE_HEIGHT + LAYOUT_CONSTANT.INSTANCE_TOP_PADDING;
|
||||
|
||||
ports.push({
|
||||
id: connection.id,
|
||||
renderName: connectionName,
|
||||
renderType: 'cellPort',
|
||||
width: LAYOUT_CONSTANT.CELL_PORT_WIDTH,
|
||||
height: LAYOUT_CONSTANT.CELL_PORT_HEIGHT,
|
||||
x: instanceWidth,
|
||||
y: offsetY
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const node = {
|
||||
id: cell.id,
|
||||
name: cell.name,
|
||||
renderName: cell.type,
|
||||
renderType: 'cell',
|
||||
width: instanceWidth,
|
||||
height: instanceHeight + LAYOUT_CONSTANT.INSTANCE_TITLE_HEIGHT + LAYOUT_CONSTANT.INSTANCE_TOP_PADDING,
|
||||
ports,
|
||||
layoutOptions: {
|
||||
// 强制固定 port 的方向
|
||||
'org.eclipse.elk.portConstraints': 'FIXED_POS',
|
||||
// TODO: 同名例化模块对齐
|
||||
'partitioning.partition': this.commonPartitionIndex
|
||||
}
|
||||
};
|
||||
|
||||
const node = this.makeInstanceNode(cell);
|
||||
nodes.push(node);
|
||||
}
|
||||
}
|
||||
@ -442,5 +297,168 @@ export class Module {
|
||||
return [nodes, edges];
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 制作器件对应的 node
|
||||
* @param {Cell} cell
|
||||
* @param {import("../skin").SkinResource} skin
|
||||
*/
|
||||
makeCellNode(cell, skin) {
|
||||
// 内置器件
|
||||
// 创建器件节点的 port, port 和 connection 一一对应
|
||||
const meta = skin.meta;
|
||||
|
||||
const height = meta.height;
|
||||
const width = meta.width;
|
||||
|
||||
const ports = [];
|
||||
|
||||
// 统计分配到左右两侧的 port
|
||||
const leftSideConnections = [];
|
||||
const rightSideConnections = [];
|
||||
|
||||
for (const conn of cell.nameToConnection.values()) {
|
||||
if (conn.direction === 'input') {
|
||||
leftSideConnections.push(conn);
|
||||
} else {
|
||||
rightSideConnections.push(conn);
|
||||
}
|
||||
}
|
||||
|
||||
// 计算左侧的
|
||||
for (let i = 0; i < leftSideConnections.length; ++ i) {
|
||||
const connection = leftSideConnections[i];
|
||||
const yOffset = meta.getPortYOffset(connection.name);
|
||||
|
||||
ports.push({
|
||||
id: connection.id,
|
||||
renderName: connection.name,
|
||||
renderType: 'cellPort',
|
||||
width: LAYOUT_CONSTANT.CELL_PORT_WIDTH,
|
||||
height: LAYOUT_CONSTANT.CELL_PORT_HEIGHT,
|
||||
x: 0,
|
||||
y: yOffset
|
||||
});
|
||||
}
|
||||
|
||||
// 计算右侧的
|
||||
for (let i = 0; i < rightSideConnections.length; ++ i) {
|
||||
const connection = rightSideConnections[i];
|
||||
const yOffset = meta.getPortYOffset(connection.name);
|
||||
|
||||
ports.push({
|
||||
id: connection.id,
|
||||
renderName: connection.name,
|
||||
renderType: 'cellPort',
|
||||
width: LAYOUT_CONSTANT.CELL_PORT_WIDTH,
|
||||
height: LAYOUT_CONSTANT.CELL_PORT_HEIGHT,
|
||||
x: width,
|
||||
y: yOffset
|
||||
});
|
||||
}
|
||||
|
||||
const node = {
|
||||
id: cell.id,
|
||||
name: cell.name,
|
||||
renderName: cell.type,
|
||||
renderType: 'cell',
|
||||
width,
|
||||
height,
|
||||
ports,
|
||||
layoutOptions: {
|
||||
'org.eclipse.elk.portConstraints': 'FIXED_POS',
|
||||
'partitioning.partition': this.commonPartitionIndex
|
||||
}
|
||||
};
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 制作例化模块对应的 node
|
||||
* @param {Cell} cell
|
||||
*/
|
||||
makeInstanceNode(cell) {
|
||||
// 统计分配到左右两侧的 port
|
||||
const leftSideConnections = [];
|
||||
const rightSideConnections = [];
|
||||
|
||||
for (const conn of cell.nameToConnection.values()) {
|
||||
if (conn.direction === 'input') {
|
||||
leftSideConnections.push(conn);
|
||||
} else {
|
||||
rightSideConnections.push(conn);
|
||||
}
|
||||
}
|
||||
|
||||
// 根据一侧的 port 数量来决定 height
|
||||
const maxPortNum = Math.max(leftSideConnections.length, rightSideConnections.length);
|
||||
const instanceHeight = Math.max(
|
||||
// 这是例化模块高度的最小值
|
||||
LAYOUT_CONSTANT.INSTANTIATION_HEIGHT,
|
||||
// 每一个 connection 占据的空间为:【上方空余区域(用来防止文本的)】 + 【本身的高度】
|
||||
(LAYOUT_CONSTANT.PORT_TOP_MARGIN + LAYOUT_CONSTANT.CELL_PORT_HEIGHT) * (maxPortNum + 1)
|
||||
);
|
||||
// 宽度等于预设宽度+两倍的padding
|
||||
const instanceWidth = LAYOUT_CONSTANT.INSTANTIATION_WIDTH + 2 * LAYOUT_CONSTANT.PORT_INNER_PADDING;
|
||||
|
||||
// 创建器件节点的 port, port 和 connection 一一对应
|
||||
const ports = [];
|
||||
let inputCount = 0;
|
||||
let outputCount = 0;
|
||||
for (const connectionName of cell.nameToConnection.keys()) {
|
||||
const connection = cell.nameToConnection.get(connectionName);
|
||||
const portSide = connection.direction === 'input' ? ELK_DIRECTION.LEFT: ELK_DIRECTION.RIGHT;
|
||||
if (connection.direction === 'input') {
|
||||
inputCount ++;
|
||||
const offsetY = inputCount * LAYOUT_CONSTANT.PORT_TOP_MARGIN +
|
||||
(inputCount - 1) * LAYOUT_CONSTANT.CELL_PORT_HEIGHT +
|
||||
LAYOUT_CONSTANT.INSTANCE_TITLE_HEIGHT + LAYOUT_CONSTANT.INSTANCE_TOP_PADDING;
|
||||
|
||||
ports.push({
|
||||
id: connection.id,
|
||||
renderName: connectionName,
|
||||
renderType: 'cellPort',
|
||||
width: LAYOUT_CONSTANT.CELL_PORT_WIDTH,
|
||||
height: LAYOUT_CONSTANT.CELL_PORT_HEIGHT,
|
||||
x: 0,
|
||||
y: offsetY
|
||||
});
|
||||
} else {
|
||||
outputCount ++;
|
||||
const offsetY = outputCount * LAYOUT_CONSTANT.PORT_TOP_MARGIN +
|
||||
(outputCount - 1) * LAYOUT_CONSTANT.CELL_PORT_HEIGHT +
|
||||
LAYOUT_CONSTANT.INSTANCE_TITLE_HEIGHT + LAYOUT_CONSTANT.INSTANCE_TOP_PADDING;
|
||||
|
||||
ports.push({
|
||||
id: connection.id,
|
||||
renderName: connectionName,
|
||||
renderType: 'cellPort',
|
||||
width: LAYOUT_CONSTANT.CELL_PORT_WIDTH,
|
||||
height: LAYOUT_CONSTANT.CELL_PORT_HEIGHT,
|
||||
x: instanceWidth,
|
||||
y: offsetY
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const node = {
|
||||
id: cell.id,
|
||||
name: cell.name,
|
||||
parentName: this.name,
|
||||
renderName: cell.type,
|
||||
renderType: 'cell',
|
||||
width: instanceWidth,
|
||||
height: instanceHeight + LAYOUT_CONSTANT.INSTANCE_TITLE_HEIGHT + LAYOUT_CONSTANT.INSTANCE_TOP_PADDING,
|
||||
ports,
|
||||
layoutOptions: {
|
||||
// 强制固定 port 的方向
|
||||
'org.eclipse.elk.portConstraints': 'FIXED_POS',
|
||||
// TODO: 同名例化模块对齐
|
||||
'partitioning.partition': this.commonPartitionIndex
|
||||
}
|
||||
};
|
||||
|
||||
return node;
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user