feat: 添加数值类型的波形显示
This commit is contained in:
		@@ -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}<br/>`;
 | 
			
		||||
          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}<br/>`;
 | 
			
		||||
            } else if (channel.type === "number") {
 | 
			
		||||
              const channelName = channel.name;
 | 
			
		||||
              const originalValue = channel.value[dataIndex];
 | 
			
		||||
              tooltip += `${channelName}: ${originalValue}<br/>`;
 | 
			
		||||
            }
 | 
			
		||||
          });
 | 
			
		||||
          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}<br/>`;
 | 
			
		||||
              } else if (channel.type === "number") {
 | 
			
		||||
                const channelName = channel.name;
 | 
			
		||||
                const originalValue = channel.value[dataIndex];
 | 
			
		||||
                tooltip += `${channelName}: ${originalValue}<br/>`;
 | 
			
		||||
              }
 | 
			
		||||
            },
 | 
			
		||||
          );
 | 
			
		||||
          return tooltip;
 | 
			
		||||
        }
 | 
			
		||||
        return "";
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
<template>
 | 
			
		||||
  <div>
 | 
			
		||||
    <div class="card">
 | 
			
		||||
    <div class="card m-5 bg-base-200 shadow-2xl">
 | 
			
		||||
      <WaveformDisplay />
 | 
			
		||||
    </div>
 | 
			
		||||
  </div>
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user