新的 skin
This commit is contained in:
parent
5a7dd7f6f0
commit
e06f6abc50
BIN
public/test.skin
BIN
public/test.skin
Binary file not shown.
@ -211,12 +211,26 @@
|
||||
|
||||
/**
|
||||
* @typedef BasicD3DataItem
|
||||
* @property {number} x
|
||||
* @property {number} y
|
||||
* @property {number} width
|
||||
* @property {number} height
|
||||
* @property {string} [fill]
|
||||
* @property {string} [text]
|
||||
* @property {number} [rx]
|
||||
* @property {number} [ry]
|
||||
* @property {string} id 全局唯一标识
|
||||
* @property {number} x svg 布局下的 x
|
||||
* @property {number} y svg 布局下的 y
|
||||
* @property {number} width 宽度
|
||||
* @property {number} height 高度
|
||||
* @property {string} [fill] 填充颜色
|
||||
* @property {string} [text] 填充文字
|
||||
* @property {number} [rx] 圆角 rx
|
||||
* @property {number} [ry] 圆角 ry
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef BasicD3ManagmentItem
|
||||
* @property {'cell' | 'connection' | 'instantiation' | 'port' | 'wire'} type
|
||||
* @property {BasicD3DataItem} data
|
||||
* @property {d3.Selection} selection
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef DragContext
|
||||
* @property {BasicD3DataItem} data
|
||||
* @property {ElkGraph} elkGraph
|
||||
*/
|
@ -16,6 +16,12 @@ export class CellRender {
|
||||
* @type {BasicD3DataItem[]}
|
||||
*/
|
||||
this.data = [];
|
||||
|
||||
/**
|
||||
* @description id 到管理数据项的映射
|
||||
* @type {Map<string, BasicD3ManagmentItem[]>}
|
||||
*/
|
||||
this.id2selections = rootRender.id2selections;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -25,6 +31,7 @@ export class CellRender {
|
||||
*/
|
||||
addAsD3DataItem(node, element) {
|
||||
this.data.push({
|
||||
id: node.id,
|
||||
element,
|
||||
x: node.x,
|
||||
y: node.y,
|
||||
@ -37,6 +44,7 @@ export class CellRender {
|
||||
render() {
|
||||
const data = this.data;
|
||||
const rootRender = this.rootRender;
|
||||
const id2selections = this.id2selections;
|
||||
|
||||
let cellSelections = this.parentSelection.selectAll('svg')
|
||||
.data(data)
|
||||
@ -59,7 +67,6 @@ export class CellRender {
|
||||
.attr('class', 'grab')
|
||||
.on('end', function (data) {
|
||||
const cellSelection = d3.select(this);
|
||||
|
||||
registerDragEvent(cellSelection, data, rootRender);
|
||||
});
|
||||
} else {
|
||||
@ -67,11 +74,22 @@ export class CellRender {
|
||||
.attr('class', 'grab')
|
||||
.each(function (data) {
|
||||
const cellSelection = d3.select(this);
|
||||
|
||||
registerDragEvent(cellSelection, data, rootRender);
|
||||
});
|
||||
}
|
||||
|
||||
cellSelections.each(function (data) {
|
||||
const selection = d3.select(this);
|
||||
if (!id2selections.has(data.id)) {
|
||||
id2selections.set(data.id, []);
|
||||
}
|
||||
id2selections.get(data.id).push({
|
||||
data,
|
||||
selection,
|
||||
type: 'cell'
|
||||
});
|
||||
});
|
||||
|
||||
this.selections = cellSelections;
|
||||
return cellSelections;
|
||||
}
|
||||
@ -89,9 +107,22 @@ export function registerDragEvent(selection, data, rootRender) {
|
||||
// 创建拖拽行为
|
||||
const drag = d3.drag();
|
||||
|
||||
drag.on('start', async event => dragStart(event, selection, data));
|
||||
drag.on('drag', async event => dragged(event, selection, data, rootRender));
|
||||
drag.on('end', async event => dragEnd(event, selection, data))
|
||||
const dragContext = {
|
||||
data: data,
|
||||
elkGraph: {
|
||||
id: 'root',
|
||||
children: [],
|
||||
edges: [],
|
||||
layoutOptions: {
|
||||
// 伟大,无需多言
|
||||
'elk.algorithm': ''
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
drag.on('start', async event => dragStart(event, selection, dragContext, rootRender));
|
||||
drag.on('drag', async event => dragged(event, selection, dragContext, rootRender));
|
||||
drag.on('end', async event => dragEnd(event, selection, dragContext, rootRender))
|
||||
|
||||
selection.call(drag);
|
||||
}
|
||||
@ -100,28 +131,36 @@ export function registerDragEvent(selection, data, rootRender) {
|
||||
*
|
||||
* @param {d3.D3DragEvent} event
|
||||
* @param {d3.Selection} selection
|
||||
* @param {DragContext} dragContext
|
||||
* @param {NetlistRender} rootRender
|
||||
*/
|
||||
function dragStart(event, selection, data) {
|
||||
function dragStart(event, selection, dragContext, rootRender) {
|
||||
selection.attr('class', 'grabbing');
|
||||
|
||||
// 更新拓扑图中各个节点的节点坐标
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {d3.D3DragEvent} event
|
||||
* @param {d3.Selection} selection
|
||||
* @param {DragContext} dragContext
|
||||
* @param {NetlistRender} rootRender
|
||||
*/
|
||||
function dragged(event, selection, data, rootRender) {
|
||||
function dragged(event, selection, dragContext, rootRender) {
|
||||
// 当拖动结束时,D3 会根据绑定的数据(data.x 和 data.y)重新渲染元素,导致元素回到初始位置。
|
||||
// 所以需要 手动更新 data.x 和 data.y
|
||||
data.x = event.x;
|
||||
data.y = event.y;
|
||||
dragContext.data.x = event.x;
|
||||
dragContext.data.y = event.y;
|
||||
|
||||
selection
|
||||
.attr('x', event.x)
|
||||
.attr('y', event.y);
|
||||
|
||||
// 根据最小拓扑图,提取出关键点,重新计算布局
|
||||
console.log(dragContext.data);
|
||||
|
||||
}
|
||||
|
||||
@ -129,7 +168,9 @@ function dragged(event, selection, data, rootRender) {
|
||||
*
|
||||
* @param {d3.D3DragEvent} event
|
||||
* @param {d3.Selection} selection
|
||||
* @param {DragContext} dragContext
|
||||
* @param {NetlistRender} rootRender
|
||||
*/
|
||||
function dragEnd(event, selection, data) {
|
||||
function dragEnd(event, selection, dragContext, rootRender) {
|
||||
selection.attr('class', 'grab');
|
||||
}
|
@ -1,17 +1,26 @@
|
||||
import * as d3 from 'd3';
|
||||
import { globalSetting } from '../global';
|
||||
import { NetlistRender } from '.';
|
||||
|
||||
export class ConnectionRender {
|
||||
/**
|
||||
* @param {d3.Selection} selection
|
||||
* @param {NetlistRender} rootRender
|
||||
*/
|
||||
constructor(selection) {
|
||||
constructor(selection, rootRender) {
|
||||
this.parentSelection = selection;
|
||||
this.rootRender = rootRender;
|
||||
|
||||
/**
|
||||
* @type {BasicD3DataItem[]}
|
||||
*/
|
||||
this.data = [];
|
||||
|
||||
/**
|
||||
* @description id 到管理数据项的映射
|
||||
* @type {Map<string, BasicD3ManagmentItem[]>}
|
||||
*/
|
||||
this.id2selections = rootRender.id2selections;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -21,6 +30,7 @@ export class ConnectionRender {
|
||||
*/
|
||||
addAsD3DataItem(cellPort, node) {
|
||||
this.data.push({
|
||||
id: cellPort.id,
|
||||
x: cellPort.x + node.x,
|
||||
y: cellPort.y + node.y + 0.5, // 0.5 是为了线宽
|
||||
width: cellPort.width,
|
||||
@ -33,6 +43,7 @@ export class ConnectionRender {
|
||||
|
||||
render() {
|
||||
const data = this.data;
|
||||
const id2selections = this.id2selections;
|
||||
|
||||
let connectionSelections = this.parentSelection.selectAll('circle')
|
||||
.data(data)
|
||||
@ -51,7 +62,18 @@ export class ConnectionRender {
|
||||
|
||||
connectionSelections
|
||||
.attr('fill', d => d.fill)
|
||||
.attr('r', d => d.r);
|
||||
.attr('r', d => d.r)
|
||||
.each(function (data) {
|
||||
const selection = d3.select(this);
|
||||
if (!id2selections.has(data.id)) {
|
||||
id2selections.set(data.id, []);
|
||||
}
|
||||
id2selections.get(data.id).push({
|
||||
data,
|
||||
selection,
|
||||
type: 'connection'
|
||||
});
|
||||
});
|
||||
|
||||
this.selections = connectionSelections;
|
||||
return connectionSelections;
|
||||
|
@ -24,7 +24,15 @@ export class NetlistRender {
|
||||
this.elkGraph = {
|
||||
id: 'root',
|
||||
children: [],
|
||||
edges: []
|
||||
edges: [],
|
||||
layoutOptions: {
|
||||
// org.eclipse. 可以去除
|
||||
'org.eclipse.elk.layered.spacing.nodeNodeBetweenLayers': 35,
|
||||
'elk.spacing.nodeNode': 35,
|
||||
'elk.layered.layering.strategy': 'NETWORK_SIMPLEX',
|
||||
'elk.algorithm': 'layered',
|
||||
'elk.partitioning.activate': true
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
@ -52,6 +60,11 @@ export class NetlistRender {
|
||||
*/
|
||||
this.startOffsetY = 0;
|
||||
|
||||
/**
|
||||
* @description id 到管理数据项的映射
|
||||
* @type {Map<string, BasicD3ManagmentItem[]>}
|
||||
*/
|
||||
this.id2selections = new Map();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -76,13 +89,6 @@ export class NetlistRender {
|
||||
const cellNodes = module.makeCellsElkNodes();
|
||||
const [constantNodes, connectionEdges] = module.makeConnectionElkNodes();
|
||||
|
||||
// debug
|
||||
for (const node of portNodes) {
|
||||
if (node.type === 'output') {
|
||||
node.x = 400;
|
||||
}
|
||||
}
|
||||
|
||||
// 挂载到渲染图中
|
||||
this.elkGraph.children.push(...portNodes);
|
||||
this.elkGraph.children.push(...cellNodes);
|
||||
@ -101,16 +107,7 @@ export class NetlistRender {
|
||||
async createLayout() {
|
||||
const graph = this.elkGraph;
|
||||
const start = performance.now();
|
||||
const layoutGraph = await this.elk.layout(graph, {
|
||||
layoutOptions: {
|
||||
// org.eclipse. 可以去除
|
||||
'org.eclipse.elk.layered.spacing.nodeNodeBetweenLayers': 35,
|
||||
'elk.spacing.nodeNode': 35,
|
||||
'elk.layered.layering.strategy': 'NETWORK_SIMPLEX',
|
||||
'elk.algorithm': 'layered',
|
||||
'elk.partitioning.activate': true
|
||||
}
|
||||
});
|
||||
const layoutGraph = await this.elk.layout(graph);
|
||||
const timecost = (performance.now() - start).toFixed(3);
|
||||
console.log(
|
||||
`%c 布局计算耗时 ${timecost} ms`,
|
||||
@ -197,10 +194,10 @@ export class NetlistRender {
|
||||
const skinManager = globalLookup.skinManager;
|
||||
|
||||
// 创建各个主要实体的 render
|
||||
this.cellRender = new CellRender(parentSelection);
|
||||
this.portRender = new PortRender(parentSelection);
|
||||
this.instantiationRender = new InstantiationRender(parentSelection);
|
||||
this.connectionRender = new ConnectionRender(parentSelection);
|
||||
this.cellRender = new CellRender(parentSelection, this);
|
||||
this.portRender = new PortRender(parentSelection, this);
|
||||
this.instantiationRender = new InstantiationRender(parentSelection, this);
|
||||
this.connectionRender = new ConnectionRender(parentSelection, this);
|
||||
|
||||
for (const node of computedLayout.children) {
|
||||
// 只计算形体的,因为 连接点 非常小,几乎不影响布局
|
||||
@ -239,8 +236,7 @@ export class NetlistRender {
|
||||
* @param {number} ratio
|
||||
*/
|
||||
async renderLine(parentSelection, computedLayout, ratio) {
|
||||
|
||||
this.wireRender = new WireRender(parentSelection);
|
||||
this.wireRender = new WireRender(parentSelection, this);
|
||||
|
||||
for (const edge of computedLayout.edges) {
|
||||
for (const section of edge.sections || []) {
|
||||
@ -250,7 +246,7 @@ export class NetlistRender {
|
||||
points.push(point);
|
||||
}
|
||||
points.push(section.endPoint);
|
||||
this.wireRender.addAsD3DataItems(points);
|
||||
this.wireRender.addAsD3DataItems(points, edge);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,17 +1,26 @@
|
||||
import * as d3 from 'd3';
|
||||
import { NetlistRender } from '.';
|
||||
|
||||
export class InstantiationRender {
|
||||
/**
|
||||
*
|
||||
* @param {d3.Selection} selection
|
||||
* @param {NetlistRender} rootRender
|
||||
*/
|
||||
constructor(selection) {
|
||||
constructor(selection, rootRender) {
|
||||
this.selection = selection;
|
||||
this.rootRender = rootRender;
|
||||
|
||||
/**
|
||||
* @type {BasicD3DataItem[]}
|
||||
*/
|
||||
this.data = []
|
||||
|
||||
/**
|
||||
* @description id 到管理数据项的映射
|
||||
* @type {Map<string, BasicD3ManagmentItem[]>}
|
||||
*/
|
||||
this.id2selections = rootRender.id2selections;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1,18 +1,28 @@
|
||||
import * as d3 from 'd3';
|
||||
import ELK from 'elkjs';
|
||||
import { globalLookup, globalSetting } from '../global';
|
||||
import { NetlistRender } from '.';
|
||||
|
||||
export class PortRender {
|
||||
/**
|
||||
*
|
||||
* @param {d3.Selection} selection
|
||||
* @param {NetlistRender} rootRender
|
||||
*/
|
||||
constructor(selection) {
|
||||
constructor(selection, rootRender) {
|
||||
this.parentSelection = selection;
|
||||
this.rootRender = rootRender;
|
||||
|
||||
/**
|
||||
* @type {BasicD3DataItem[]}
|
||||
*/
|
||||
this.data = [];
|
||||
|
||||
/**
|
||||
* @description id 到管理数据项的映射
|
||||
* @type {Map<string, BasicD3ManagmentItem[]>}
|
||||
*/
|
||||
this.id2selections = rootRender.id2selections;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -21,6 +31,7 @@ export class PortRender {
|
||||
*/
|
||||
addAsD3DataItem(node) {
|
||||
this.data.push({
|
||||
id: node.id,
|
||||
x: node.x,
|
||||
y: node.y,
|
||||
width: node.width,
|
||||
@ -34,6 +45,7 @@ export class PortRender {
|
||||
|
||||
render() {
|
||||
const data = this.data;
|
||||
const id2selections = this.id2selections;
|
||||
|
||||
let portSelections = this.parentSelection.selectAll('g.port')
|
||||
.data(data)
|
||||
@ -50,9 +62,9 @@ export class PortRender {
|
||||
let texts = portSelections.append('text')
|
||||
.attr('x', data => data.width / 2) // 文本的 x 坐标(居中)
|
||||
.attr('y', data => data.height / 2) // 文本的 y 坐标(居中)
|
||||
.attr('dominant-baseline', 'middle') // 文本垂直居中
|
||||
.attr('text-anchor', 'middle') // 文本水平居中
|
||||
.attr('fill', 'white') // 文本颜色
|
||||
.attr('dominant-baseline', 'middle') // 文本垂直居中
|
||||
.attr('text-anchor', 'middle') // 文本水平居中
|
||||
.attr('fill', 'var(--foreground)') // 文本颜色
|
||||
.attr('font-size', '0')
|
||||
.transition()
|
||||
.duration(1000)
|
||||
@ -68,28 +80,26 @@ export class PortRender {
|
||||
.attr('stroke-width', 2)
|
||||
.attr('rx', d => d.rx)
|
||||
.attr('ry', d => d.ry);
|
||||
|
||||
portSelections
|
||||
.attr('class', 'grab')
|
||||
.each(function (data) {
|
||||
const portSelection = d3.select(this);
|
||||
registerDragEvent(portSelection, data);
|
||||
});
|
||||
} else {
|
||||
ports.attr('stroke', 'var(--main-color)')
|
||||
.attr('stroke-width', 2)
|
||||
.attr('rx', d => d.rx)
|
||||
.attr('ry', d => d.ry);
|
||||
|
||||
portSelections
|
||||
.attr('class', 'grab')
|
||||
.each(function (data) {
|
||||
const portSelection = d3.select(this);
|
||||
registerDragEvent(portSelection, data);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
portSelections
|
||||
.attr('class', 'grab')
|
||||
.each(function (data) {
|
||||
const portSelection = d3.select(this);
|
||||
// 注册拖拽
|
||||
registerDragEvent(portSelection, data);
|
||||
// 进行管理
|
||||
if (!id2selections.has(data.id)) {
|
||||
id2selections.set(data.id, []);
|
||||
}
|
||||
id2selections.get(data.id).push({ data, selection: portSelection });
|
||||
});
|
||||
|
||||
this.selections = portSelections;
|
||||
return portSelections;
|
||||
}
|
||||
|
@ -1,27 +1,38 @@
|
||||
import * as d3 from 'd3';
|
||||
import { globalSetting } from '../global';
|
||||
import { NetlistRender } from '.';
|
||||
|
||||
export class WireRender {
|
||||
/**
|
||||
*
|
||||
* @param {d3.Selection} selection
|
||||
* @param {d3.Selection} selection
|
||||
* @param {NetlistRender} rootRender
|
||||
*/
|
||||
constructor(selection) {
|
||||
constructor(selection, rootRender) {
|
||||
this.selection = selection;
|
||||
this.rootRender = rootRender;
|
||||
|
||||
/**
|
||||
* @type {BasicD3DataItem[]}
|
||||
*/
|
||||
this.data = [];
|
||||
|
||||
/**
|
||||
* @description id 到管理数据项的映射
|
||||
* @type {Map<string, BasicD3ManagmentItem[]>}
|
||||
*/
|
||||
this.id2selections = rootRender.id2selections;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 将 elknode 关于 wire 的数据添加为 d3 数据项目
|
||||
* @param {ElkPoint[]} points
|
||||
* @param {ElkEdge} edge
|
||||
*/
|
||||
addAsD3DataItems(points) {
|
||||
addAsD3DataItems(points, edge) {
|
||||
for (let i = 0; i < points.length - 1; ++ i) {
|
||||
this.data.push({
|
||||
id: edge.id,
|
||||
x1: points[i].x,
|
||||
y1: points[i].y,
|
||||
x2: points[i + 1].x,
|
||||
@ -34,6 +45,7 @@ export class WireRender {
|
||||
|
||||
render() {
|
||||
const data = this.data;
|
||||
const id2selections = this.id2selections;
|
||||
|
||||
let lineSelections = this.selection.selectAll('line')
|
||||
.data(data)
|
||||
@ -51,7 +63,14 @@ export class WireRender {
|
||||
.duration(1000);
|
||||
}
|
||||
|
||||
lineSelections.attr('stroke-width', data => data.strokeWidth);
|
||||
// line 就不注册拖拽事件了
|
||||
lineSelections
|
||||
.attr('stroke-width', data => data.strokeWidth)
|
||||
.each(function (data) {
|
||||
const selection = d3.select(this);
|
||||
if (!id2selections.has(data.id)) {
|
||||
id2selections.set(data.id, []);
|
||||
}
|
||||
id2selections.get(data.id).push({ data, selection });
|
||||
});
|
||||
}
|
||||
}
|
@ -65,9 +65,13 @@ class SkinMeta {
|
||||
const parser = new DOMParser();
|
||||
|
||||
// 颜色替换
|
||||
// 填充颜色
|
||||
svgString = svgString.replace(/#279BB0/g, 'var(--main-dark-color)');
|
||||
// 边缘颜色
|
||||
svgString = svgString.replace(/#000000/g, 'var(--main-color)');
|
||||
|
||||
// 字体颜色
|
||||
svgString = svgString.replace(/#010101/g, 'var(--foreground)');
|
||||
|
||||
const svgDoc = parser.parseFromString(svgString, 'image/svg+xml');
|
||||
const element = svgDoc.documentElement;
|
||||
this.width = element.getAttribute('width');
|
||||
|
Loading…
x
Reference in New Issue
Block a user