实验性功能新增「均值滤波」+ 构建为新信号

- 「实验性功能」二级菜单新增带勾选的「均值滤波」:开启后为本网格每条信号叠加绘制
  一条 N 点滑动平均曲线,算法与「边沿计数(中心轴)」的中心平均值曲线**完全一致**
  (同一实现 ComputeMovingAverage),配色同样由信号色派生为高对比度颜色、透明度一致。
- 新增均值滤波浮动面板:标题栏可拖拽、✕ 关闭,面板内列出被处理的信号名、
  「均值点数 N」内联编辑行,以及底部的「构建为新信号」按钮(悬停高亮 + 手型光标)。
- 「构建为新信号」:把滤波后的曲线作为新信号插入,命名为「<原信号名称> (N点均值)」,
  归属到原信号所属组的原信号位置**下方**,成为独立的新信号;其所在网格也移动到
  原信号网格的下方。合并网格时一次按钮为该网格内每条可见信号各构建一条。
  为此新增 DataSet::InsertDerivedSignal / TabConfig::RemapAfterSignalInsert /
  TabConfig::MoveLaneOfSignalBelow / TabConfig::RenameLanes(插入位置之后的信号引用
  与可见性列同步重映射)。
- 光标交互与「边沿计数(中心轴)」同构:光标处标注均值滤波曲线的取值
  与双光标增量,文案为「值(均值滤波)」/「值(+增量 均值滤波)」;
  两条派生曲线的取值胶囊已抽成同一段绘制逻辑。
- 浮动面板种类由 bool centerAxis 改为 EdgePanelKind{COUNT,CENTER,MEAN},
  布局/宽度/命中测试/绘制统一走同一套几何。
- 以 VERY_BIG_DATA.CSV 验证:曲线预览、N 编辑、双光标取值标注、
  单信号与合并网格两种情况下的新信号命名与插入位置均正确。
This commit is contained in:
DeepSeek V4.1 Flash committed 2026-10-09 21:00:38 +08:00
1 parent 8ae8d7af4f
commit f5028b1e18
6 files changed
+885 -210

No files matched your search

+80 -8
View File
@@ -53,7 +53,8 @@ namespace jdi
LANE_Y_MAX, /**< 编辑某网格的 Y 轴最大值。 */
LANE_Y_MIN, /**< 编辑某网格的 Y 轴最小值。 */
SIGNAL_NAME, /**< 编辑图例中某信号的显示名称。 */
EDGE_CENTER_SMOOTH /**< 编辑「中心轴边沿计数」的平滑点数 N。 */
EDGE_CENTER_SMOOTH, /**< 编辑「中心轴边沿计数」的平滑点数 N。 */
MEAN_FILTER_RADIUS /**< 编辑「均值滤波」的均值点数 N。 */
};
/**
@@ -69,7 +70,18 @@ namespace jdi
PLOT_CMD_LANE_AUTO_VIEW = 6, /**< 切换本网格的「选区 Y 轴自适应」开关。 */
PLOT_CMD_LANE_EDGE_COUNT = 7, /**< 切换本网格的「实验性功能 → 边沿计数」开关。 */
PLOT_CMD_LANE_EDGE_CENTER = 8, /**< 切换本网格的「实验性功能 → 边沿计数(中心轴)」开关。 */
PLOT_CMD_LANE_SPLIT = 9 /**< 拆分当前网格:把网格内信号按个拆成独立网格并顺次下移编号。 */
PLOT_CMD_LANE_SPLIT = 9, /**< 拆分当前网格:把网格内信号按个拆成独立网格并顺次下移编号。 */
PLOT_CMD_LANE_MEAN_FILTER = 10 /**< 切换本网格的「实验性功能 → 均值滤波」开关。 */
};
/**
* @brief 实验性功能浮动面板的种类。
*/
enum class EdgePanelKind : int
{
COUNT = 0, /**< 边沿计数。 */
CENTER = 1, /**< 边沿计数(中心轴)。 */
MEAN = 2 /**< 均值滤波。 */
};
/**
@@ -462,6 +474,40 @@ namespace jdi
*/
bool ToggleLaneEdgeCenterCount(int laneIndex);
/**
* @brief 判断某网格是否开启了「实验性功能 → 均值滤波」。
* @param [IN] laneIndex 网格下标。
* @return 开启返回 true。
*/
bool LaneMeanFilter(int laneIndex) const;
/**
* @brief 设置某网格的「均值滤波」开关。
* @param [IN] laneIndex 网格下标。
* @param [IN] enabled 目标状态。
* @return 状态发生变化返回 true。
*/
bool SetLaneMeanFilter(int laneIndex, bool enabled);
/**
* @brief 切换某网格的「均值滤波」开关。
* @param [IN] laneIndex 网格下标。
* @return 切换后的状态。
*/
bool ToggleLaneMeanFilter(int laneIndex);
/**
* @brief 取出「构建为新信号」请求(由面板底部按钮触发)。
*
* 请求只描述「哪一网格、用多大的 N」,真正的信号构建与插入由宿主(Application)
* 完成——数据层属于 DataSet,绘图区不能直接修改它。
*
* @param [OUT] laneIndex 触发该请求的网格下标。
* @param [OUT] radius 均值滤波半径 N。
* @return 存在待处理请求返回 true(取出后清空)。
*/
bool TakePendingMeanFilterBuild(int& laneIndex, int& radius);
/**
* @brief 进入某网格 Y 轴上限/下限的内联输入状态。
* @param [IN] laneIndex 网格下标。
@@ -913,7 +959,7 @@ namespace jdi
bool IsOnEdgeCursorLine(int x, int y, int& laneIndex) const;
/**
* @brief 实验性功能浮动面板的几何布局(边沿计数 / 中心轴边沿计数共用同一套样式)。
* @brief 实验性功能浮动面板的几何布局(三类面板共用同一套样式)。
*/
struct EdgePanelLayout
{
@@ -921,16 +967,17 @@ namespace jdi
gd::RectF title; /**< 标题栏(**唯一可拖拽区域**)。 */
gd::RectF close; /**< ✕ 关闭按钮。 */
gd::RectF edit; /**< 面板底部编辑行(无编辑内容时宽度为 0)。 */
gd::RectF button; /**< 面板底部按钮(无按钮时宽度为 0)。 */
int rows = 0; /**< 统计行数(每条可见信号一行)。 */
};
/**
* @brief 计算某个实验性功能面板的布局。
* @param [IN] laneIndex 网格下标。
* @param [IN] centerAxis 为 true 时取「边沿计数(中心轴)」面板,否则取「边沿计数」。
* @param [IN] kind 面板种类。
* @return 布局;面板未展开时各项均为空矩形。
*/
EdgePanelLayout EdgePanelLayoutFor(int laneIndex, bool centerAxis) const;
EdgePanelLayout EdgePanelLayoutFor(int laneIndex, EdgePanelKind kind) const;
/**
* @brief 获取「边沿计数(中心轴)」浮动面板的矩形。
@@ -984,6 +1031,28 @@ namespace jdi
void PaintCenterCurve(gd::Graphics& g, const ResolvedSignal& resolved, const LaneVisual& lane,
int smooth) const;
/**
* @brief 绘制某条信号的均值滤波曲线(与中心平均值曲线同一算法与同一配色派生规则)。
* @param [IN,OUT] g 绘图上下文。
* @param [IN] resolved 信号视图。
* @param [IN] lane 网格绘制信息。
* @param [IN] radius 均值滤波半径 N。
*/
void PaintMeanFilterCurve(gd::Graphics& g, const ResolvedSignal& resolved, const LaneVisual& lane,
int radius) const;
/**
* @brief 取某个时刻的均值滤波曲线取值(用于纵向光标的取值标注)。
* @param [IN] file 信号所属文件。
* @param [IN] signal 目标信号。
* @param [IN] radius 均值滤波半径 N。
* @param [IN] time 采样时刻。
* @param [OUT] value 输出取值。
* @return 取到有效值返回 true。
*/
bool MeanFilterValueAt(const DataFile& file, const SignalData& signal, int radius, double time,
double& value) const;
/**
* @brief 数据点标记的位置与"越界方向"(用于决定画圆点还是贴边三角)。
*/
@@ -1066,10 +1135,10 @@ namespace jdi
/**
* @brief 计算实验性功能面板所需的宽度(排版与绘制共用,保证文字完整不被省略)。
* @param [IN] laneIndex 网格下标。
* @param [IN] centerAxis 为 true 时取「边沿计数(中心轴)」面板。
* @param [IN] kind 面板种类。
* @return 面板宽度(像素)。
*/
float EdgePanelWidth(int laneIndex, bool centerAxis) const;
float EdgePanelWidth(int laneIndex, EdgePanelKind kind) const;
/**
* @brief 判断边沿统计是否限定在双光标区间内(双光标模式)。
@@ -1205,6 +1274,9 @@ namespace jdi
std::wstring m_pendingRenameName; /**< 待宿主窗口应用的信号新名称。 */
bool m_pendingRename = false; /**< 是否存在待应用的重命名请求。 */
int m_pendingMeanFilterLane = -1; /**< 待构建的均值滤波信号所属网格下标(-1 表示无请求)。 */
int m_pendingMeanFilterRadius = 0; /**< 待构建的均值滤波半径 N。 */
int m_hoverLane = -1; /**< 当前悬停网格下标。 */
int m_mouseX = 0; /**< 最近一次鼠标横坐标(用于悬停提示定位)。 */
int m_mouseY = 0; /**< 最近一次鼠标纵坐标(用于悬停提示定位)。 */
@@ -1257,7 +1329,7 @@ namespace jdi
// 「边沿计数」横向光标 / 面板的拖拽状态。
int m_edgeDragLane = -1; /**< 正在拖动的横向光标所属网格。 */
int m_edgePanelDragLane = -1; /**< 正在拖动的统计面板所属网格。 */
bool m_edgePanelDragCenter = false; /**< 正在拖动的面板是否为「中心轴边沿计数」面板。 */
EdgePanelKind m_edgePanelDragKind = EdgePanelKind::COUNT; /**< 正在拖动的面板种类。 */
gd::PointF m_edgePanelDragOffset; /**< 抓取面板时的相对偏移(保持不跳变)。 */
/**