2024-12-30 07:42:10 +08:00

223 lines
7.0 KiB
JavaScript

/* eslint-disable */
import * as d3 from 'd3';
import { globalSetting } from '../global';
import { NetlistRender } from '.';
import { LAYOUT_CONSTANT, LINE_WIDTH } from './layout';
import { svgResource } from '../skin/draw';
export class WireRender {
/**
*
* @param {d3.Selection} selection
* @param {NetlistRender} rootRender
*/
constructor(selection, rootRender) {
this.selection = selection;
this.rootRender = rootRender;
/**
* @type {BasicD3DataItem[]}
*/
this.data = [];
/**
* @description id 到管理数据项的映射
* @type {Map<string, BasicD3ManagmentItem[]>}
*/
this.id2manager = rootRender.id2manager;
this.arrowHeight = 12;
this.arrowWidth = 12;
}
/**
* @description 将 elknode 关于 wire 的数据添加为 d3 数据项目
* @param {ElkPoint[]} points
* @param {ElkEdge} edge
*/
addAsD3DataItems(points, edge) {
const linePaths = [];
for (let i = 0; i < points.length; ++ i) {
// 根据点的信息创建 path
const command = i === 0 ? 'M': 'L';
const x = points[i].x;
const y = points[i].y;
linePaths.push(`${command}${x},${y}`);
}
const lineSvg = linePaths.join(' ');
// 判断当前的朝向
const endPoint = points.at(-1);
const direction = endPoint.x > points.at(-2).x ? 'right' : 'left';
const arrowX = direction === 'right' ? endPoint.x - LAYOUT_CONSTANT.CELL_PORT_WIDTH - this.arrowWidth:
endPoint.x + LAYOUT_CONSTANT.CELL_PORT_WIDTH
const arrowY = endPoint.y - this.arrowHeight / 2;
this.data.push({
svg: lineSvg,
endPoint: points.at(-1),
arrow: {
icon: direction + '-arrow',
x: arrowX,
y: arrowY,
width: this.arrowWidth,
height: this.arrowHeight
},
active: false
});
}
render() {
const data = this.data;
const id2manager = this.id2manager;
const _this = this;
const arrowHeight = 12;
const arrowWidth = 12;
let lineSelections = this.selection.selectAll('path.lines')
.data(data)
.enter()
.append('path')
.attr('d', d => d.svg)
.attr('class', 'connection-line')
.attr("fill", "none")
.attr('stroke', 'var(--wire-color)');
let arrowSelections = this.selection.selectAll('svg.line-arrow')
.data(data)
.enter()
.append(data => {
const arrowInfo = data.arrow;
// 获取箭头 svg
const element = svgResource.get(arrowInfo.icon);
element.setAttribute('x', arrowInfo.x);
element.setAttribute('y', arrowInfo.y);
element.setAttribute('width', arrowInfo.width);
element.setAttribute('height', arrowInfo.height);
element.setAttribute('opacity', 'var(--line-arrow-opacity)');
return element;
});
arrowSelections.raise();
const id2animation = new Map();
lineSelections.on('click', function(_, data) {
data.active = !data.active;
if (data.active) {
const pathSelection = d3.select(this);
// 如果当前激活,显示数据流向
const pathLength = pathSelection.node().getTotalLength();
const pivot = _this.selection
.append('circle')
.attr('r', 6)
.attr('fill', 'var(--wire-ball-color)');
// 进行一次动画
function renderOneAnimation() {
console.log(pathLength);
// 1400 的长度差不多配 3500 ?
const duration = pathLength / 14 * 35;
let transition = pivot.transition().duration(duration);
transition.attrTween('transform', () => {
return t => {
const x = cubicBezierAnimation(t, 0, pathLength);
const point = pathSelection.node().getPointAtLength(x);
return `translate(${point.x},${point.y})`;
}
});
transition = transition.on('end', function() {
if (id2animation.has(data.id)) {
renderOneAnimation();
}
});
// 动画结束后重新启动动画
id2animation.set(data.id, {
pivot,
transition
});
}
renderOneAnimation();
} else {
if (id2animation.has(data.id)) {
const { pivot, transition } = id2animation.get(data.id);
pivot.remove();
id2animation.delete(data.id);
}
}
});
lineSelections.on('mouseenter', function(_, data) {
const selection = d3.select(this);
// 移动到最上层
selection.raise();
selection.attr('stroke', 'var(--wire-active-color)');
});
lineSelections.on('mouseleave', function(_, data) {
const selection = d3.select(this);
selection.attr('stroke', 'var(--wire-color)');
});
if (globalSetting.renderAnimation) {
lineSelections = lineSelections
.transition()
.duration(1000);
}
lineSelections
.attr('stroke-width', LINE_WIDTH)
.each(function (data) {
const selection = d3.select(this);
// 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.get(data.id).push(managerItem);
return managerItem;
}
}
/**
* @description 计算贝塞尔插值,输入 0 - 1 的值 (delta),输出 [oldVal, newVal] 中间的插值
* @param {*} delta
* @param {*} oldVal
* @param {*} newVal
* @returns
*/
function cubicBezierAnimation(delta, oldVal, newVal) {
delta = 3 * (1 - delta) * (1 - delta) * delta + 3 * (1 - delta) * delta * delta + delta * delta * delta;
return (1 - delta) * oldVal + delta * newVal;
}