更新架构,进行依赖注入

This commit is contained in:
锦恢 2024-12-26 14:14:58 +08:00
parent e06f6abc50
commit b718780cf5
7 changed files with 190 additions and 101 deletions

View File

@ -223,14 +223,14 @@
*/
/**
* @typedef BasicD3ManagmentItem
* @typedef BasicD3ManagmentItem 用于管理一个 实体/连接 d3 上下文数据结构
* @property {'cell' | 'connection' | 'instantiation' | 'port' | 'wire'} type
* @property {BasicD3DataItem} data
* @property {d3.Selection} selection
* @property {BasicD3DataItem} data 用于绑定 d3 渲染的数据初始化/特殊事件会使得 d3 去主动根据
* @property {d3.Selection} selection 渲染成图形对应的 d3 选择集
* @property {DragContext} [dragContext] 与当前这个 实体 相关的拖拽上下文目前不支持 wire connection
*/
/**
* @typedef DragContext
* @property {BasicD3DataItem} data
* @property {ElkGraph} elkGraph
*/

View File

@ -21,7 +21,7 @@ export class CellRender {
* @description id 到管理数据项的映射
* @type {Map<string, BasicD3ManagmentItem[]>}
*/
this.id2selections = rootRender.id2selections;
this.id2manager = rootRender.id2manager;
}
/**
@ -44,7 +44,8 @@ export class CellRender {
render() {
const data = this.data;
const rootRender = this.rootRender;
const id2selections = this.id2selections;
const id2manager = this.id2manager;
const _this = this;
let cellSelections = this.parentSelection.selectAll('svg')
.data(data)
@ -67,74 +68,83 @@ export class CellRender {
.attr('class', 'grab')
.on('end', function (data) {
const cellSelection = d3.select(this);
registerDragEvent(cellSelection, data, rootRender);
const manager = _this.createDataManager(cellSelection, data);
registerDragEvent(manager, rootRender);
});
} else {
cellSelections = cellSelections
.attr('class', 'grab')
.each(function (data) {
const cellSelection = d3.select(this);
registerDragEvent(cellSelection, data, rootRender);
const manager = _this.createDataManager(cellSelection, data);
registerDragEvent(manager, 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;
}
/**
*
* @param {d3.Selection} selection
* @param {BasicD3DataItem} data
* @returns {BasicD3ManagmentItem}
*/
createDataManager(selection, data) {
const id2manager = this.id2manager;
// 创建拖拽上下文
const dragContext = {
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.set(data.id, managerItem);
return managerItem;
}
}
/**
* @description 注册关于 器件 的拖动事件
*
* 需要提取最小拓扑子图然后重新调整各个区域的尺寸
* @param {d3.Selection} selection
* @param {any} data
* @param {BasicD3ManagmentItem} manager
* @param {NetlistRender} rootRender
*/
export function registerDragEvent(selection, data, rootRender) {
export function registerDragEvent(manager, rootRender) {
// 创建拖拽行为
const drag = d3.drag();
const dragContext = {
data: data,
elkGraph: {
id: 'root',
children: [],
edges: [],
layoutOptions: {
// 伟大,无需多言
'elk.algorithm': ''
}
}
}
drag.on('start', async event => dragStart(event, manager, rootRender));
drag.on('drag', async event => dragged(event, manager, rootRender));
drag.on('end', async event => dragEnd(event, manager, rootRender))
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);
manager.selection.call(drag);
}
/**
*
* @param {d3.D3DragEvent} event
* @param {d3.Selection} selection
* @param {DragContext} dragContext
* @param {BasicD3ManagmentItem} manager
* @param {NetlistRender} rootRender
*/
function dragStart(event, selection, dragContext, rootRender) {
function dragStart(event, manager, rootRender) {
const selection = manager.selection;
selection.attr('class', 'grabbing');
// 更新拓扑图中各个节点的节点坐标
@ -145,32 +155,34 @@ function dragStart(event, selection, dragContext, rootRender) {
/**
*
* @param {d3.D3DragEvent} event
* @param {d3.Selection} selection
* @param {DragContext} dragContext
* @param {BasicD3ManagmentItem} manager
* @param {NetlistRender} rootRender
*/
function dragged(event, selection, dragContext, rootRender) {
function dragged(event, manager, rootRender) {
// 当拖动结束时D3 会根据绑定的数据data.x 和 data.y重新渲染元素导致元素回到初始位置。
// 所以需要 手动更新 data.x 和 data.y
dragContext.data.x = event.x;
dragContext.data.y = event.y;
const selection = manager.selection;
const data = manager.data;
data.x = event.x;
data.y = event.y;
selection
.attr('x', event.x)
.attr('y', event.y);
// 根据最小拓扑图,提取出关键点,重新计算布局
console.log(dragContext.data);
const context = manager.dragContext;
console.log(context.elkGraph);
}
/**
*
* @param {d3.D3DragEvent} event
* @param {d3.Selection} selection
* @param {DragContext} dragContext
* @param {BasicD3ManagmentItem} manager
* @param {NetlistRender} rootRender
*/
function dragEnd(event, selection, dragContext, rootRender) {
function dragEnd(event, manager, rootRender) {
const selection = manager.selection;
selection.attr('class', 'grab');
}

View File

@ -20,7 +20,7 @@ export class ConnectionRender {
* @description id 到管理数据项的映射
* @type {Map<string, BasicD3ManagmentItem[]>}
*/
this.id2selections = rootRender.id2selections;
this.id2manager = rootRender.id2manager;
}
/**
@ -43,7 +43,8 @@ export class ConnectionRender {
render() {
const data = this.data;
const id2selections = this.id2selections;
const id2manager = this.id2manager;
const _this = this;
let connectionSelections = this.parentSelection.selectAll('circle')
.data(data)
@ -65,17 +66,33 @@ export class ConnectionRender {
.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'
});
const manager = _this.createDataManager(selection, data);
});
this.selections = connectionSelections;
return connectionSelections;
}
/**
*
* @param {d3.Selection} selection
* @param {BasicD3DataItem} data
* @returns {BasicD3ManagmentItem}
*/
createDataManager(selection, data) {
const id2manager = this.id2manager;
// connection 不需要拖拽上下文
const managerItem = {
data,
selection,
type: 'connection'
};
if (!id2manager.has(data.id)) {
id2manager.set(data.id, []);
}
id2manager.set(data.id, managerItem);
return managerItem;
}
}

View File

@ -62,9 +62,13 @@ export class NetlistRender {
/**
* @description id 到管理数据项的映射
*
* - key: id, layout.js 中的赋值 id 一致全局唯一
* - value: 每一个 id 对应的管理者这里存储着数据视图和渲染视图查询与操作DOM一体一个 ID 对应的真实渲染体可能不只一个
* 比如对于 wire一个 id 对应的 wire 可以是连续的折现但是这些折现对应的渲染体是复数个的
* @type {Map<string, BasicD3ManagmentItem[]>}
*/
this.id2selections = new Map();
this.id2manager = new Map();
}
/**

View File

@ -20,7 +20,7 @@ export class InstantiationRender {
* @description id 到管理数据项的映射
* @type {Map<string, BasicD3ManagmentItem[]>}
*/
this.id2selections = rootRender.id2selections;
this.id2manager = rootRender.id2manager;
}
/**

View File

@ -22,7 +22,7 @@ export class PortRender {
* @description id 到管理数据项的映射
* @type {Map<string, BasicD3ManagmentItem[]>}
*/
this.id2selections = rootRender.id2selections;
this.id2manager = rootRender.id2manager;
}
/**
@ -45,7 +45,9 @@ export class PortRender {
render() {
const data = this.data;
const id2selections = this.id2selections;
const id2manager = this.id2manager;
const rootRender = this.rootRender;
const _this = this;
let portSelections = this.parentSelection.selectAll('g.port')
.data(data)
@ -60,8 +62,8 @@ export class PortRender {
.attr('fill', d => d.fill);
let texts = portSelections.append('text')
.attr('x', data => data.width / 2) // 文本的 x 坐标(居中)
.attr('y', data => data.height / 2) // 文本的 y 坐标(居中)
.attr('x', data => data.width / 2) // 文本的 x 坐标(居中)
.attr('y', data => data.height / 2) // 文本的 y 坐标(居中)
.attr('dominant-baseline', 'middle') // 文本垂直居中
.attr('text-anchor', 'middle') // 文本水平居中
.attr('fill', 'var(--foreground)') // 文本颜色
@ -70,7 +72,7 @@ export class PortRender {
.duration(1000)
.attr('font-size', '12px')
.attr('class', 'port-caption')
.text(data => data.text); // 设置文本内容
.text(data => data.text); // 设置文本内容
if (globalSetting.renderAnimation) {
@ -91,18 +93,46 @@ export class PortRender {
.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 });
const manager = _this.createDataManager(portSelection, data);
registerDragEvent(manager, rootRender);
});
this.selections = portSelections;
return portSelections;
}
/**
*
* @param {d3.Selection} selection
* @param {BasicD3DataItem} data
* @returns {BasicD3ManagmentItem}
*/
createDataManager(selection, data) {
const id2manager = this.id2manager;
// 创建拖拽上下文
const dragContext = {
elkGraph: {
// elk 是无状态的id 取什么名字都行
id: 'root',
children: [],
edges: [],
layoutOptions: {}
}
}
const managerItem = {
data,
selection,
type: 'port',
dragContext
};
if (!id2manager.has(data.id)) {
id2manager.set(data.id, []);
}
id2manager.set(data.id, managerItem);
return managerItem;
}
}
@ -112,45 +142,50 @@ export class PortRender {
* @description 注册关于 器件 的拖动事件
*
* 需要提取最小拓扑子图然后重新调整各个区域的尺寸
* @param {d3.Selection} selection
* @param {any} data
* @param {BasicD3ManagmentItem} manager
* @param {NetlistRender} rootRender
*/
export function registerDragEvent(selection, data) {
export function registerDragEvent(manager, rootRender) {
// 创建拖拽行为
const drag = d3.drag();
drag.on("start", event => dragStart(event, selection, data));
drag.on("drag", event => dragged(event, selection, data));
drag.on("end", event => dragEnd(event, selection, data));
drag.on("start", event => dragStart(event, manager, rootRender));
drag.on("drag", event => dragged(event, manager, rootRender));
drag.on("end", event => dragEnd(event, manager, rootRender));
selection.call(drag);
manager.selection.call(drag);
}
/**
*
* @param {d3.D3DragEvent} event
* @param {d3.Selection} selection
* @param {BasicD3ManagmentItem} manager
* @param {NetlistRender} rootRender
*/
function dragStart(event, selection, data) {
function dragStart(event, manager, rootRender) {
const selection = manager.selection;
selection.attr('class', 'grabbing');
}
/**
*
* @param {d3.D3DragEvent} event
* @param {d3.Selection} selection
* @param {BasicD3ManagmentItem} manager
* @param {NetlistRender} rootRender
*/
function dragged(event, selection, data) {
function dragged(event, manager, rootRender) {
const selection = manager.selection;
const data = manager.data;
data.x = event.x;
data.y = event.y;
selection.attr("transform", `translate(${event.x}, ${event.y})`);
}
/**
*
* @param {d3.D3DragEvent} event
* @param {d3.Selection} selection
* @param {BasicD3ManagmentItem} manager
* @param {NetlistRender} rootRender
*/
function dragEnd(event, selection, data) {
function dragEnd(event, manager, rootRender) {
const selection = manager.selection;
selection.attr('class', 'grab');
}

View File

@ -21,7 +21,7 @@ export class WireRender {
* @description id 到管理数据项的映射
* @type {Map<string, BasicD3ManagmentItem[]>}
*/
this.id2selections = rootRender.id2selections;
this.id2manager = rootRender.id2manager;
}
/**
@ -45,7 +45,8 @@ export class WireRender {
render() {
const data = this.data;
const id2selections = this.id2selections;
const id2manager = this.id2manager;
const _this = this;
let lineSelections = this.selection.selectAll('line')
.data(data)
@ -67,10 +68,30 @@ export class WireRender {
.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 });
const manager = _this.createDataManager(selection, data);
});
}
/**
*
* @param {d3.Selection} selection
* @param {BasicD3DataItem} data
* @returns {BasicD3ManagmentItem}
*/
createDataManager(selection, data) {
const id2manager = this.id2manager;
// wire 不需要拖拽上下文
const managerItem = {
data,
selection,
type: 'wire'
};
if (!id2manager.has(data.id)) {
id2manager.set(data.id, []);
}
id2manager.set(data.id, managerItem);
return managerItem;
}
}