From 45619460cf4a70c243327cb1d072b855b47b7bd9 Mon Sep 17 00:00:00 2001
From: Kirigaya <1193466151@qq.com>
Date: Tue, 24 Dec 2024 21:40:27 +0800
Subject: [PATCH] =?UTF-8?q?=E5=AE=8C=E6=88=90=E7=BC=A9=E6=94=BE=E5=92=8C?=
=?UTF-8?q?=E5=B9=B3=E7=A7=BB?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
public/netlist.css | 2 +-
src/components/render/index.vue | 17 ++-
src/components/setting/index.vue | 14 +++
src/hook/css.js | 1 +
src/hook/global.js | 20 +++-
src/hook/render/drag.js | 14 +++
src/hook/render/index.js | 186 +++++++++++++++++++++++--------
src/hook/render/layout.js | 22 +---
src/hook/wheel-event.js | 104 +++++++++++++++++
src/i18n/ar.json | 3 +-
src/i18n/de.json | 3 +-
src/i18n/en.json | 5 +-
src/i18n/fr.json | 3 +-
src/i18n/ja.json | 3 +-
src/i18n/ko.json | 3 +-
src/i18n/ru.json | 3 +-
src/i18n/zh-cn.json | 3 +-
src/i18n/zh-tw.json | 3 +-
18 files changed, 328 insertions(+), 81 deletions(-)
create mode 100644 src/hook/render/drag.js
create mode 100644 src/hook/wheel-event.js
diff --git a/public/netlist.css b/public/netlist.css
index 4b6155e..ae10844 100644
--- a/public/netlist.css
+++ b/public/netlist.css
@@ -125,4 +125,4 @@ a {
.el-select__wrapper.is-disabled {
opacity: 0.6;
box-shadow: unset !important;
-}
\ No newline at end of file
+}
diff --git a/src/components/render/index.vue b/src/components/render/index.vue
index 410222d..449b502 100644
--- a/src/components/render/index.vue
+++ b/src/components/render/index.vue
@@ -1,14 +1,26 @@
@@ -20,5 +32,6 @@ defineComponent({ name: 'netlist-render' });
display: flex;
justify-content: center;
align-items: center;
+ transition: var(--animation-5s);
}
\ No newline at end of file
diff --git a/src/components/setting/index.vue b/src/components/setting/index.vue
index 889bc83..ce715e5 100644
--- a/src/components/setting/index.vue
+++ b/src/components/setting/index.vue
@@ -40,6 +40,20 @@
+
+
+
+
+ {{ t('render-animation') }}
+
+
+
+
diff --git a/src/hook/css.js b/src/hook/css.js
index 2ad9ef1..95a5c14 100644
--- a/src/hook/css.js
+++ b/src/hook/css.js
@@ -19,6 +19,7 @@ export function setDefaultCss() {
document.body.style.setProperty('--el-color-info-light-9', 'var(--vscode-focusBorder)');
document.body.style.setProperty('--el-color-info', 'var(--foreground)');
document.body.style.setProperty('--el-color-info-light-8', 'var(--vscode-focusBorder)');
+
// document.body.style.setProperty('--el-color-white', 'var(--background)');
// 设置全局宏
diff --git a/src/hook/global.js b/src/hook/global.js
index 7cd65c5..46f3044 100644
--- a/src/hook/global.js
+++ b/src/hook/global.js
@@ -7,7 +7,8 @@ import { NetlistRender } from './render';
export const emitter = mitt();
export const globalSetting = reactive({
- language: 'zh'
+ language: 'zh',
+ renderAnimation: true
});
export const globalLookup = {
@@ -19,7 +20,22 @@ export const globalLookup = {
/**
* @type {NetlistRender}
*/
- netlistRender: new NetlistRender()
+ netlistRender: new NetlistRender(),
+
+ /**
+ * @type {number}
+ */
+ svgScale: 1,
+
+ /**
+ * @type {number}
+ */
+ svgTranslateX: 0,
+
+ /**
+ * @type {number}
+ */
+ svgTranslateY: 0
};
function loadSetting() {
diff --git a/src/hook/render/drag.js b/src/hook/render/drag.js
new file mode 100644
index 0000000..d253758
--- /dev/null
+++ b/src/hook/render/drag.js
@@ -0,0 +1,14 @@
+import * as d3 from 'd3';
+import ELK from 'elkjs';
+
+/**
+ * @description 注册关于 器件 的拖动事件
+ *
+ * 需要提取最小拓扑子图,然后重新调整各个区域的尺寸
+ * @param {d3.Transition} renderSvg
+ */
+export function registerCellDragEvent(renderSvg) {
+ // renderSvg.call(d3.drag().on('drag', (event, g) => {
+ // console.log(event);
+ // }));
+}
\ No newline at end of file
diff --git a/src/hook/render/index.js b/src/hook/render/index.js
index d323385..5d34ddf 100644
--- a/src/hook/render/index.js
+++ b/src/hook/render/index.js
@@ -3,7 +3,8 @@ import ELK from 'elkjs';
import { Module } from './layout';
-import { globalLookup } from '../global';
+import { globalLookup, globalSetting } from '../global';
+import { registerCellDragEvent } from './drag';
export class NetlistRender {
/**
@@ -77,7 +78,6 @@ export class NetlistRender {
'org.eclipse.elk.layered.layering.strategy': 'LONGEST_PATH'
}
});
- console.log(layoutGraph);
return layoutGraph;
}
@@ -96,12 +96,15 @@ export class NetlistRender {
const ratio = this.renderHeight / virtualHeight;
// 遍历计算布局进行创建
- const svg = d3.select(container).append('svg')
+ const svg = d3.select(container)
+ .selectAll('svg')
.attr('width', virtualWidth)
.attr('height', virtualHeight);
-
- await this.renderEntity(svg, computedLayout, ratio);
+
await this.renderLine(svg, computedLayout, ratio);
+ await this.renderEntity(svg, computedLayout, ratio);
+
+ this.selection = svg;
return svg;
}
@@ -121,12 +124,15 @@ export class NetlistRender {
// 生成用于绘制的 d3 数据结构
// 默认需要渲染成矩形的(缺失样式的器件、例化模块等等)
const squares = [];
+
+ const connections = [];
const svgElements = [];
const skinManager = globalLookup.skinManager;
for (const node of computedLayout.children) {
const skin = skinManager.querySkin(node.renderName);
if (skin) {
+ // 具有 skin 的器件
svgElements.push({
element: skin.meta.svgDoc.documentElement,
x: node.x,
@@ -136,6 +142,7 @@ export class NetlistRender {
fill: 'var(--main-dark-color)',
});
} else {
+ // 没有 skin 的器件
squares.push({
x: node.x,
y: node.y,
@@ -150,54 +157,126 @@ export class NetlistRender {
// 如果存在 port,绘制 port
for (const cellPort of node.ports || []) {
- squares.push({
+ connections.push({
x: cellPort.x + node.x,
- y: cellPort.y + node.y,
+ y: cellPort.y + node.y + 0.5, // 0.5 是为了线宽
width: cellPort.width,
height: cellPort.height,
fill: 'var(--main-color)',
text: '',
- rx: 0,
- ry: 0
+ r: 3.5
});
}
}
- svg.selectAll('rect')
- .data(squares)
- .enter()
- .append('rect')
- .attr('x', data => data.x)
- .attr('y', data => data.y)
- .attr('width', data => data.width)
- .attr('height', data => data.height)
- .attr('fill', d => d.fill)
- .attr('stroke', 'var(--main-color)')
- .attr('stroke-width', 2)
- .attr('rx', d => d.rx)
- .attr('ry', d => d.ry);
-
- svg.selectAll('g')
- .data(svgElements)
- .enter()
- .append(data => {
- const element = data.element;
- element.setAttribute('x', data.x);
- element.setAttribute('y', data.y);
- return element;
- });
+ if (globalSetting.renderAnimation) {
+ svg.selectAll('rect')
+ .data(squares)
+ .enter()
+ .append('rect')
+ .attr('x', data => data.x)
+ .attr('y', data => data.y)
+ .attr('width', data => data.width)
+ .attr('height', data => data.height)
+ .attr('fill', d => d.fill)
+ .transition()
+ .duration(1000)
+ .attr('stroke', 'var(--main-color)')
+ .attr('stroke-width', 2)
+ .attr('rx', d => d.rx)
+ .attr('ry', d => d.ry);
+
+ const renderSvg = svg.selectAll('g')
+ .data(svgElements)
+ .enter()
+ .append(data => {
+ const element = data.element;
+ element.setAttribute('x', data.x);
+ element.setAttribute('y', data.y);
+ element.setAttribute('stroke-opacity', 0);
+ return element;
+ })
+ .transition()
+ .duration(1000)
+ .attr('stroke-opacity', 1);
+
+ registerCellDragEvent(renderSvg);
- svg.selectAll('text')
- .data(squares)
- .enter()
- .append('text')
- .attr('x', data => data.x + data.width / 2) // 文本的 x 坐标(居中)
- .attr('y', data => data.y + data.height / 2) // 文本的 y 坐标(居中)
- .attr('dominant-baseline', 'middle') // 文本垂直居中
- .attr('text-anchor', 'middle') // 文本水平居中
- .attr('fill', 'white') // 文本颜色
- .attr('font-size', '12px')
- .text(data => data.text); // 设置文本内容
+ svg.selectAll('circle')
+ .data(connections)
+ .enter()
+ .append('circle')
+ .attr('cx', data => data.x)
+ .attr('cy', data => data.y)
+ .attr('width', data => data.width)
+ .attr('height', data => data.height)
+ .transition()
+ .duration(1000)
+ .attr('fill', d => d.fill)
+ .attr('r', d => d.r);
+
+ svg.selectAll('text')
+ .data(squares)
+ .enter()
+ .append('text')
+ .attr('x', data => data.x + data.width / 2) // 文本的 x 坐标(居中)
+ .attr('y', data => data.y + data.height / 2) // 文本的 y 坐标(居中)
+ .attr('dominant-baseline', 'middle') // 文本垂直居中
+ .attr('text-anchor', 'middle') // 文本水平居中
+ .attr('fill', 'white') // 文本颜色
+ .attr('font-size', '0')
+ .transition()
+ .duration(1000)
+ .attr('font-size', '12px')
+ .text(data => data.text); // 设置文本内容
+ } else {
+ svg.selectAll('rect')
+ .data(squares)
+ .enter()
+ .append('rect')
+ .attr('x', data => data.x)
+ .attr('y', data => data.y)
+ .attr('width', data => data.width)
+ .attr('height', data => data.height)
+ .attr('fill', d => d.fill)
+ .attr('stroke', 'var(--main-color)')
+ .attr('stroke-width', 2)
+ .attr('rx', d => d.rx)
+ .attr('ry', d => d.ry);
+
+ svg.selectAll('g')
+ .data(svgElements)
+ .enter()
+ .append(data => {
+ const element = data.element;
+ element.setAttribute('x', data.x);
+ element.setAttribute('y', data.y);
+ return element;
+ });
+
+ svg.selectAll('circle')
+ .data(connections)
+ .enter()
+ .append('circle')
+ .attr('cx', data => data.x)
+ .attr('cy', data => data.y)
+ .attr('width', data => data.width)
+ .attr('height', data => data.height)
+ .attr('fill', d => d.fill)
+ .attr('r', d => d.r);
+
+ svg.selectAll('text')
+ .data(squares)
+ .enter()
+ .append('text')
+ .attr('x', data => data.x + data.width / 2) // 文本的 x 坐标(居中)
+ .attr('y', data => data.y + data.height / 2) // 文本的 y 坐标(居中)
+ .attr('dominant-baseline', 'middle') // 文本垂直居中
+ .attr('text-anchor', 'middle') // 文本水平居中
+ .attr('fill', 'white') // 文本颜色
+ .attr('font-size', '12px')
+ .text(data => data.text); // 设置文本内容
+ }
}
/**
@@ -230,7 +309,7 @@ export class NetlistRender {
}
}
- svg.selectAll('line')
+ let lineSelection = svg.selectAll('line')
.data(lines)
.enter()
.append('line')
@@ -238,7 +317,24 @@ export class NetlistRender {
.attr('y1', data => data.y1)
.attr('x2', data => data.x2)
.attr('y2', data => data.y2)
- .attr('stroke-width', data => data.strokeWidth)
.attr('stroke', data => data.color);
+
+ if (globalSetting.renderAnimation) {
+ lineSelection = lineSelection.transition().duration(1000);
+ }
+
+ lineSelection.attr('stroke-width', data => data.strokeWidth);
+ }
+
+ /**
+ * @description 从 globalLookup 中更新 svg 的方位
+ */
+ updateLocationFromGlobal() {
+ const svg = globalLookup.netlistRender.selection;
+ if (!svg) {
+ return;
+ }
+
+ svg.attr('transform', `translate(${globalLookup.svgTranslateX}, ${globalLookup.svgTranslateY}) scale(${globalLookup.svgScale})`);
}
}
\ No newline at end of file
diff --git a/src/hook/render/layout.js b/src/hook/render/layout.js
index d9f3ee1..3aa740d 100644
--- a/src/hook/render/layout.js
+++ b/src/hook/render/layout.js
@@ -134,16 +134,6 @@ export class Module {
for (let i = 0; i < leftSideConnections.length; ++ i) {
const connection = leftSideConnections[i];
const yOffset = meta.getPortYOffset(connection.name) * SKIN_SCALE;
- const targetY = yOffset;
-
- // 默认采用 JUSTIFIED,均分一侧的 height
- // 我们绘制的 svg 的 port offset y 未必和 elk 分配得到的一样,因此需要进行修正
- const layoutY = height / (leftSideConnections.length + 1) * (i + 1);
- const extraOffset = LINE_WIDTH * leftSideConnections.length;
- const anchorY = layoutY - targetY;
-
- console.log(yOffset);
-
ports.push({
id: connection.id,
@@ -161,17 +151,7 @@ export class Module {
// 计算右侧的
for (let i = 0; i < rightSideConnections.length; ++ i) {
const connection = rightSideConnections[i];
- const yOffset = meta.getPortYOffset(connection.name) * SKIN_SCALE;
- const targetY = yOffset;
-
- // 默认采用 JUSTIFIED,均分一侧的 height
- // 我们绘制的 svg 的 port offset y 未必和 elk 分配得到的一样,因此需要进行修正
- const layoutY = height / (rightSideConnections.length + 1) * (i + 1);
- const extraOffset = LINE_WIDTH * rightSideConnections.length / 2;
- const anchorY = targetY - layoutY + extraOffset;
-
- console.log(yOffset);
-
+ const yOffset = meta.getPortYOffset(connection.name) * SKIN_SCALE;
ports.push({
id: connection.id,
diff --git a/src/hook/wheel-event.js b/src/hook/wheel-event.js
new file mode 100644
index 0000000..5035b35
--- /dev/null
+++ b/src/hook/wheel-event.js
@@ -0,0 +1,104 @@
+import { globalLookup } from "./global";
+
+const MAX_SCALE = 10.0;
+const MIN_SCALE = 0.0;
+
+/**
+ *
+ * @param {HTMLElement} element
+ * @param {WheelEvent} event
+ */
+function windowsKeydown(element, event) {
+ const svg = globalLookup.netlistRender.selection;
+ if (!svg) {
+ return;
+ }
+
+ const { deltaX, deltaY } = event;
+ if (event.ctrlKey) {
+ if (deltaY < 0) {
+ // scale up
+ const nextScale = globalLookup.svgScale + 0.1;
+ if (nextScale <= MAX_SCALE) {
+ globalLookup.svgScale = nextScale;
+ globalLookup.netlistRender.updateLocationFromGlobal();
+ }
+
+ } else if (deltaY > 0) {
+ // scale down
+ const nextScale = globalLookup.svgScale - 0.1;
+ if (nextScale >= MIN_SCALE) {
+ globalLookup.svgScale = nextScale;
+ globalLookup.netlistRender.updateLocationFromGlobal();
+ }
+ }
+
+ event.preventDefault();
+ } else if (event.shiftKey) {
+ event.preventDefault();
+ } else if (deltaX !== 0 && deltaY === 0) {
+ if (deltaX > 0) {
+ // scroll left
+ const translateX = globalLookup.svgTranslateX - 50;
+ globalLookup.svgTranslateX = translateX;
+ globalLookup.netlistRender.updateLocationFromGlobal();
+ } else {
+ // scroll right
+ const translateX = globalLookup.svgTranslateX + 50;
+ globalLookup.svgTranslateX = translateX;
+ globalLookup.netlistRender.updateLocationFromGlobal();
+ }
+
+ event.preventDefault();
+ } else if (deltaX === 0 && deltaY !== 0) {
+ if (deltaY > 0) {
+ // scroll up
+ const translateY = globalLookup.svgTranslateY - 50;
+ globalLookup.svgTranslateY = translateY;
+ globalLookup.netlistRender.updateLocationFromGlobal();
+ } else {
+ // scroll down
+ const translateY = globalLookup.svgTranslateY + 50;
+ globalLookup.svgTranslateY = translateY;
+ globalLookup.netlistRender.updateLocationFromGlobal();
+ }
+
+ event.preventDefault();
+ }
+}
+
+/**
+ *
+ * @param {HTMLElement} element
+ * @param {WheelEvent} event
+ */
+function linuxKeydown(element, event) {
+
+}
+
+/**
+ *
+ * @param {HTMLElement} element
+ * @param {WheelEvent} event
+ */
+function macOsKeydown(element, event) {
+
+}
+
+/**
+ * @description 注册滚轮事件
+ * @param {HTMLElement} element
+ * @returns {(event: WheelEvent) => void}
+ */
+export function registerWheelEvent(element) {
+ const platform = navigator.platform;
+ if (platform.startsWith('Win')) {
+ return event => windowsKeydown(element, event);
+ } else if (platform.startsWith('Linux')) {
+ return event => linuxKeydown(element, event);
+ } else if (platform.startsWith('Mac')) {
+ return event => macOsKeydown(element, event);
+ } else {
+ throw Error('不支持的操作系统!');
+ }
+}
diff --git a/src/i18n/ar.json b/src/i18n/ar.json
index 1a5ecdb..8b71648 100644
--- a/src/i18n/ar.json
+++ b/src/i18n/ar.json
@@ -8,5 +8,6 @@
"cancel": "إلغاء",
"tips": "نصائح",
"language-setting": "اللغة",
- "setting.language.change-dialog": "لقد قمت بتغيير اللغة إلى {0} ، ونوصي بإعادة تشغيل Netlist Viewer"
+ "setting.language.change-dialog": "لقد قمت بتغيير اللغة إلى {0} ، ونوصي بإعادة تشغيل Netlist Viewer",
+ "render-animation": "تفعيل الرسوم المتحركة للعرض"
}
\ No newline at end of file
diff --git a/src/i18n/de.json b/src/i18n/de.json
index 2819da6..8e966a4 100644
--- a/src/i18n/de.json
+++ b/src/i18n/de.json
@@ -8,5 +8,6 @@
"cancel": "Abbrechen",
"tips": "Tipps",
"language-setting": "Sprache",
- "setting.language.change-dialog": "Sie haben die Sprache auf {0} geändert. Wir empfehlen Ihnen, Netlist Viewer neu zu starten."
+ "setting.language.change-dialog": "Sie haben die Sprache auf {0} geändert. Wir empfehlen Ihnen, Netlist Viewer neu zu starten.",
+ "render-animation": "Rendering-Animation aktivieren"
}
\ No newline at end of file
diff --git a/src/i18n/en.json b/src/i18n/en.json
index 927d7c5..c4b136b 100644
--- a/src/i18n/en.json
+++ b/src/i18n/en.json
@@ -3,10 +3,11 @@
"general-setting": "General",
"appearance-setting": "Appearance",
"current-version": "current version",
- "copyright": "The copyright of this software belongs to Digital-IDE project team. Welcome to Star.",
+ "copyright": "The copyright of this software belongs to Digital-IDE project team. Welcome to Star.",
"confirm": "confirm",
"cancel": "cancel",
"tips": "Tips",
"language-setting": "Language",
- "setting.language.change-dialog": "You have changed the language to {0}, we recommend restarting Netlist Viewer."
+ "setting.language.change-dialog": "You have changed the language to {0}, we recommend restarting Netlist Viewer.",
+ "render-animation": "enable rendering animation"
}
\ No newline at end of file
diff --git a/src/i18n/fr.json b/src/i18n/fr.json
index bbf7f63..d466186 100644
--- a/src/i18n/fr.json
+++ b/src/i18n/fr.json
@@ -8,5 +8,6 @@
"cancel": "Annuler",
"tips": "Conseils",
"language-setting": "Langue",
- "setting.language.change-dialog": "Vous avez changé la langue en {0}, nous vous recommandons de redémarrer Netlist Viewer."
+ "setting.language.change-dialog": "Vous avez changé la langue en {0}, nous vous recommandons de redémarrer Netlist Viewer.",
+ "render-animation": "Activer l'animation de rendu"
}
\ No newline at end of file
diff --git a/src/i18n/ja.json b/src/i18n/ja.json
index 3073cda..3f59426 100644
--- a/src/i18n/ja.json
+++ b/src/i18n/ja.json
@@ -8,5 +8,6 @@
"cancel": "キャンセル",
"tips": "ヒント",
"language-setting": "言語",
- "setting.language.change-dialog": "言語を {0} に変更しました。Netlist Viewer を再起動することをお勧めします。"
+ "setting.language.change-dialog": "言語を {0} に変更しました。Netlist Viewer を再起動することをお勧めします。",
+ "render-animation": "レンダリングアニメーションを有効にする"
}
\ No newline at end of file
diff --git a/src/i18n/ko.json b/src/i18n/ko.json
index fba49ec..53d8422 100644
--- a/src/i18n/ko.json
+++ b/src/i18n/ko.json
@@ -8,5 +8,6 @@
"cancel": "취소",
"tips": "팁",
"language-setting": "언어",
- "setting.language.change-dialog": "언어를 {0} 으로 변경했습니다. Netlist Viewer 를 다시 시작하는 것을 권장합니다."
+ "setting.language.change-dialog": "언어를 {0} 으로 변경했습니다. Netlist Viewer 를 다시 시작하는 것을 권장합니다.",
+ "render-animation": "렌더링 애니메이션 활성화"
}
\ No newline at end of file
diff --git a/src/i18n/ru.json b/src/i18n/ru.json
index 8eb884e..aa6712a 100644
--- a/src/i18n/ru.json
+++ b/src/i18n/ru.json
@@ -8,5 +8,6 @@
"cancel": "Отменить",
"tips": "Советы",
"language-setting": "Язык",
- "setting.language.change-dialog": "Вы изменили язык на {0}, мы рекомендуем перезапустить Netlist Viewer."
+ "setting.language.change-dialog": "Вы изменили язык на {0}, мы рекомендуем перезапустить Netlist Viewer.",
+ "render-animation": "Включить анимацию рендеринга"
}
\ No newline at end of file
diff --git a/src/i18n/zh-cn.json b/src/i18n/zh-cn.json
index 10ce1c8..140795f 100644
--- a/src/i18n/zh-cn.json
+++ b/src/i18n/zh-cn.json
@@ -8,5 +8,6 @@
"cancel": "取消",
"tips": "提示",
"language-setting": "语言",
- "setting.language.change-dialog": "您已经更换语言为 {0} ,我们建议您重启 Netlist Viewer"
+ "setting.language.change-dialog": "您已经更换语言为 {0} ,我们建议您重启 Netlist Viewer",
+ "render-animation": "开启渲染动画"
}
\ No newline at end of file
diff --git a/src/i18n/zh-tw.json b/src/i18n/zh-tw.json
index e1c5f5d..2b254d1 100644
--- a/src/i18n/zh-tw.json
+++ b/src/i18n/zh-tw.json
@@ -8,5 +8,6 @@
"cancel": "取消",
"tips": "提示",
"language-setting": "語言",
- "setting.language.change-dialog": "您已將語言更改為 {0} ,我們建議您重新啟動 Netlist Viewer。"
+ "setting.language.change-dialog": "您已將語言更改為 {0} ,我們建議您重新啟動 Netlist Viewer。",
+ "render-animation": "開啟渲染動畫"
}
\ No newline at end of file