diff --git a/src/components/WaveformDisplay/WaveformDisplay.vue b/src/components/WaveformDisplay/WaveformDisplay.vue
index e73f4c3..d443c08 100644
--- a/src/components/WaveformDisplay/WaveformDisplay.vue
+++ b/src/components/WaveformDisplay/WaveformDisplay.vue
@@ -39,6 +39,7 @@ import {
DataZoomComponent,
AxisPointerComponent,
ToolboxComponent,
+ MarkLineComponent,
} from "echarts/components";
import { CanvasRenderer } from "echarts/renderers";
import type { ComposeOption } from "echarts/core";
@@ -48,6 +49,7 @@ import type {
TooltipComponentOption,
GridComponentOption,
DataZoomComponentOption,
+ MarkLineComponentOption,
} from "echarts/components";
import type {
ToolboxComponentOption,
@@ -66,6 +68,7 @@ use([
DataZoomComponent,
LineChart,
CanvasRenderer,
+ MarkLineComponent,
]);
type EChartsOption = ComposeOption<
@@ -75,6 +78,7 @@ type EChartsOption = ComposeOption<
| GridComponentOption
| DataZoomComponentOption
| LineSeriesOption
+ | MarkLineComponentOption
>;
const analyzer = useRequiredInjection(useWaveformManager);
@@ -90,7 +94,9 @@ const option = computed((): EChartsOption => {
if (isUndefined(analyzer.logicData.value)) return {};
// 只获取启用的通道,使用y数据结构
- const enabledChannels = analyzer.logicData.value.y.filter(channel => channel.enabled);
+ const enabledChannels = analyzer.logicData.value.y.filter(
+ (channel) => channel.enabled,
+ );
const enabledChannelIndices = analyzer.logicData.value.y
.map((channel, index) => (channel.enabled ? index : -1))
.filter((index) => index !== -1);
@@ -149,77 +155,141 @@ const option = computed((): EChartsOption => {
// 创建系列数据
const series: LineSeriesOption[] = [];
- enabledChannelIndices.forEach((originalIndex: number, displayIndex: number) => {
- const channel = analyzer.logicData.value!.y[originalIndex];
- if (channel.type === "logic") {
- // logic类型,原样显示
- series.push({
- name: channel.name,
- type: "line",
- data: channel.value.map(
- (value: number) => value + displayIndex * channelSpacing + 0.2,
- ),
- step: "end",
- lineStyle: {
- width: 2,
- color: channel.color,
- },
- areaStyle: {
- opacity: 0.3,
- origin: displayIndex * channelSpacing,
- color: channel.color,
- },
- symbol: "none",
- sampling: "lttb",
- animation: false,
- });
- } else if (channel.type === "number") {
- // number类型,VCD仿真样式:两个线条(1和0),变化时有斜率(过渡点),无areaStyle
- const values = channel.value;
- const xArr = analyzer.logicData.value!.x;
- // 构造带过渡的点序列
- function buildVcdLine(valArr: number[], high: number, low: number) {
- const points: {x: number, y: number}[] = [];
- let lastValue = high;
- points.push({x: xArr[0], y: lastValue});
- for (let i = 1; i < valArr.length; i++) {
- const v = valArr[i] !== valArr[i-1] ? (lastValue === high ? low : high) : lastValue;
- points.push({x: xArr[i], y: v});
- lastValue = v;
+ enabledChannelIndices.forEach(
+ (originalIndex: number, displayIndex: number) => {
+ const channel = analyzer.logicData.value!.y[originalIndex];
+ if (channel.type === "logic") {
+ // logic类型,原样显示
+ series.push({
+ name: channel.name,
+ type: "line",
+ data: channel.value.map(
+ (value: number) => value + displayIndex * channelSpacing + 0.2,
+ ),
+ step: "end",
+ lineStyle: {
+ width: 2,
+ color: channel.color,
+ },
+ areaStyle: {
+ opacity: 0.3,
+ origin: displayIndex * channelSpacing,
+ color: channel.color,
+ },
+ symbol: "none",
+ sampling: "lttb",
+ animation: false,
+ });
+ } else if (channel.type === "number") {
+ const values = channel.value;
+ const xArr = analyzer.logicData.value!.x;
+ // 构造带过渡的点序列
+ function buildVcdLine(valArr: number[], high: number, low: number) {
+ const points: { x: number; y: number }[] = [];
+ let lastValue = high;
+ points.push({ x: xArr[0], y: lastValue });
+ for (let i = 1; i < valArr.length; i++) {
+ const v =
+ valArr[i] !== valArr[i - 1]
+ ? lastValue === high
+ ? low
+ : high
+ : lastValue;
+ points.push({ x: xArr[i], y: v });
+ lastValue = v;
+ }
+ return points.map((p) => p.y);
}
- // 返回y数组,x由category轴控制
- return points.map(p => p.y);
+
+ // 计算变化点区间
+ function buildMarkLines(valArr: number[], yBase: number) {
+ const markLines: any[] = [];
+ let lastValue = valArr[0];
+ let lastIdx = 0;
+ // 格式化函数
+ function formatValue(val: number) {
+ if (channel.base === "hex") return "0x" + val.toString(16).toUpperCase();
+ if (channel.base === "bin") return "0b" + val.toString(2);
+ return val.toString();
+ }
+ for (let i = 1; i <= valArr.length; i++) {
+ if (i === valArr.length || valArr[i] !== lastValue) {
+ markLines.push([
+ {
+ xAxis: lastIdx,
+ yAxis: yBase,
+ label: {
+ formatter: formatValue(lastValue),
+ position: "insideMiddle",
+ color: channel.color,
+ fontSize: 18,
+ opacity: 1,
+ },
+ lineStyle: {
+ opacity: 0,
+ },
+ },
+ {
+ xAxis: i - 1,
+ yAxis: yBase,
+ lineStyle: {
+ opacity: 0,
+ },
+ },
+ ]);
+ lastValue = valArr[i];
+ lastIdx = i;
+ }
+ }
+ return markLines;
+ }
+
+ // 1线条
+ series.push({
+ name: channel.name + "_1",
+ type: "line",
+ data: buildVcdLine(
+ values,
+ displayIndex * channelSpacing + 1,
+ displayIndex * channelSpacing,
+ ),
+ step: false,
+ lineStyle: {
+ width: 2,
+ color: channel.color,
+ },
+ symbol: "none",
+ sampling: "lttb",
+ animation: false,
+ // 添加markLine
+ markLine: {
+ data: buildMarkLines(values, displayIndex * channelSpacing + 0.5),
+ emphasis: {
+ disabled: true,
+ },
+ },
+ });
+ // 0线条
+ series.push({
+ name: channel.name + "_0",
+ type: "line",
+ data: buildVcdLine(
+ values,
+ displayIndex * channelSpacing,
+ displayIndex * channelSpacing + 1,
+ ),
+ step: false,
+ lineStyle: {
+ width: 2,
+ color: channel.color,
+ },
+ symbol: "none",
+ sampling: "lttb",
+ animation: false,
+ });
}
- // 1线条
- series.push({
- name: channel.name + "_1",
- type: "line",
- data: buildVcdLine(values, displayIndex * channelSpacing + 1, displayIndex * channelSpacing),
- step: false, // 关闭step,允许斜率
- lineStyle: {
- width: 2,
- color: channel.color,
- },
- symbol: "none",
- sampling: "lttb",
- animation: false,
- });
- // 0线条
- series.push({
- name: channel.name + "_0",
- type: "line",
- data: buildVcdLine(values, displayIndex * channelSpacing, displayIndex * channelSpacing + 1),
- step: false,
- lineStyle: {
- width: 2,
- color: channel.color,
- },
- symbol: "none",
- sampling: "lttb",
- animation: false,
- });
- }
- });
+ },
+ );
return {
animation: false,
@@ -237,18 +307,20 @@ const option = computed((): EChartsOption => {
const timeValue = analyzer.logicData.value!.x[params[0].dataIndex];
const dataIndex = params[0].dataIndex;
let tooltip = `Time: ${timeValue.toFixed(3)}${analyzer.logicData.value!.xUnit}
`;
- enabledChannelIndices.forEach((originalIndex: number, displayIndex: number) => {
- const channel = analyzer.logicData.value!.y[originalIndex];
- if (channel.type === "logic") {
- const channelName = channel.name;
- const originalValue = channel.value[dataIndex];
- tooltip += `${channelName}: ${originalValue}
`;
- } else if (channel.type === "number") {
- const channelName = channel.name;
- const originalValue = channel.value[dataIndex];
- tooltip += `${channelName}: ${originalValue}
`;
- }
- });
+ enabledChannelIndices.forEach(
+ (originalIndex: number, displayIndex: number) => {
+ const channel = analyzer.logicData.value!.y[originalIndex];
+ if (channel.type === "logic") {
+ const channelName = channel.name;
+ const originalValue = channel.value[dataIndex];
+ tooltip += `${channelName}: ${originalValue}
`;
+ } else if (channel.type === "number") {
+ const channelName = channel.name;
+ const originalValue = channel.value[dataIndex];
+ tooltip += `${channelName}: ${originalValue}
`;
+ }
+ },
+ );
return tooltip;
}
return "";
diff --git a/src/views/Project/Debugger.vue b/src/views/Project/Debugger.vue
index 786ff44..be6e578 100644
--- a/src/views/Project/Debugger.vue
+++ b/src/views/Project/Debugger.vue
@@ -1,6 +1,6 @@