实验性功能:移除「差异放大」、新增「弱波增强」;修复放大后拖拽光标越出视界

- 移除差异放大:ComputeDiffAmplify、EdgePanelKind::DIFF_AMP 面板、DIFF_AMP_*
  编辑目标、菜单项与相关缓存,并删除仅供它使用的 SlidingMax / SlidingExtrema /
  float 版滑动平均。
- 新增弱波增强 ComputeWeakWaveBoost(values, level, out)(O(n)):
  1) 对轻度平滑(5 点滑动平均)后的信号扫描单调段,方向翻转处即为波峰/波谷;
  2) 波峰低于左右两个波峰均值(波谷高于左右两个波谷均值)即判为弱波;
  3) 波峰目标 = 较小波峰 + 等级×(较大−较小);
     波谷目标 = 较大波谷 − 等级×(较大−较小);
  4) 极值点取目标值,两侧数据点在相邻极值点的偏移量之间用平滑步进过渡,
     因此健康周期逐位不变、数据端点原样保留。
- 弱波增强面板:逐信号复选框 + 「弱波增强等级」滑块(0–100%,整个网格共用)
  + 「构建为新信号」按钮;构建名形如「<原信号> (弱波增强 M%)」。
  等级进 SceneContentHash,改动即触发重绘;Lane 新增
  weakBoost/weakBoostLevel/weakBoostItems,并在 LaneCarry、SplitLane、
  RemapAfterSignalInsert/Removal 中同步。
- 修复:放大到一定程度后左右拖拽时间光标(尤其抓取较宽的读数标签、抓取偏移较大时)
  会把光标时间推到视界之外;现在 CURSOR_MOVE 与 CURSOR_SPAN_MOVE 结束后
  按可见区间夹取(视图变化仍不移动光标,保留贴边读数与方向三角)。
- 离屏验证(窗口隐藏,不占用鼠标/键盘/屏幕):295 项检查全部通过,
  含等级 0/50/100% 的目标值与健康区逐位不变、滑块拖拽与左右钳制、
  合成纹波端到端恢复(弱周期峰峰值 100→160,健康周期 200 不变)、
  光标拖拽始终落在可见区间内。
This commit is contained in:
DeepSeek V4.1 Flash committed 2026-10-10 18:46:01 +08:00
1 parent d4206a8d67
commit 67f659ad23
6 files changed
+811 -764

No files matched your search

+56 -71
View File
@@ -374,13 +374,13 @@ namespace jdi
using EdgeCenterItem = MeanFilterItem;
/**
* @brief 「差异放大」中某个信号自己的参数。
* @brief 「弱波增强」中某个信号自己的条目。
*
* 字段含义与均值滤波/中心轴不同(但同样是「一个信号 + 两个整数参数」,
* 因此复用同一结构以共享面板、缓存与引用重映射的实现):
* `points` = 窗口点数 X(趋势窗口半径),`order` = 放大倍数 N。
* 目前只用到 `ref`(增强等级属于整个网格,统一保存在 `weakBoostLevel` 中);
* 复用 `MeanFilterItem` 结构是为了与均值滤波/中心轴共享
* 「条目查找 + 信号插入/删除时的引用重映射」等既有实现。
*/
using DiffAmpItem = MeanFilterItem;
using WeakBoostItem = MeanFilterItem;
std::wstring name; /**< 网格名称。 */
std::vector<SignalRef> signals; /**< 该网格包含的信号(按加入顺序)。 */
@@ -501,46 +501,44 @@ namespace jdi
double meanFilterPanelY = -1.0;
/**
* @brief 「实验性功能 → 差异放大」开关。
* @brief 「实验性功能 → 弱波增强」开关。
*
* 开启后为本网格内**被勾选**的信号额外绘制一条「差异放大」曲线:
* 把信号与局部趋势线的差值按“局部差异强度”归一化后放大,
* 使不明显的下凹(如纹波里的弱 M 形)变明显,而差异已经很大的区域**逐点不变**。
* 开启后为本网格内**被勾选**的信号额外绘制一条「弱波增强」曲线:
* 识别纹波的波峰/波谷,把“比左右同类极值更弱”的波峰上拉、波谷下拉,
* 并按「弱波增强等级」在左右两个同类极值之间取目标值,使其恢复与周边一致的振幅。
* 这条曲线只是叠加显示,不会作为信号加入左侧列表。
*/
bool diffAmp = false;
bool weakBoost = false;
/**
* @brief 参与差异放大预览的信号及其参数(只包含被勾选的信号)。
*
* `points` = 窗口点数 X(局部差异强度统计半径,约取纹波周期的一半),
* `order` = 放大倍数 N(最大放大倍数)。
*/
std::vector<DiffAmpItem> diffAmpItems;
/** 弱波增强等级(0–1),0% = 目标取左右极值中较小/较大的那一个,100% = 取另一个。 */
double weakBoostLevel = 0.5;
/** 取某个信号的差异放大参数(未勾选时返回空指针)。 */
const DiffAmpItem* FindDiffAmpItem(const SignalRef& ref) const;
/** 参与弱波增强预览的信号条目(只包含被勾选的信号)。 */
std::vector<WeakBoostItem> weakBoostItems;
/** 取得或创建某信号的差异放大参数(用于勾选)。 */
DiffAmpItem& EnsureDiffAmpItem(const SignalRef& ref);
/** 取某个信号的弱波增强条目(未勾选时返回空指针)。 */
const WeakBoostItem* FindWeakBoostItem(const SignalRef& ref) const;
/** 取消某信号的差异放大勾选。 */
void RemoveDiffAmpItem(const SignalRef& ref);
/** 取得或创建某信号的弱波增强条目(用于勾选)。 */
WeakBoostItem& EnsureWeakBoostItem(const SignalRef& ref);
/** 取某信号的差异放大勾选状态。 */
bool DiffAmpEnabled(const SignalRef& ref) const
/** 取消某信号的弱波增强勾选。 */
void RemoveWeakBoostItem(const SignalRef& ref);
/** 取某信号的弱波增强勾选状态。 */
bool WeakBoostEnabled(const SignalRef& ref) const
{
return FindDiffAmpItem(ref) != nullptr;
return FindWeakBoostItem(ref) != nullptr;
}
/** 差异放大浮动面板是否展开。 */
bool diffAmpPanelOpen = true;
/** 弱波增强浮动面板是否展开。 */
bool weakBoostPanelOpen = true;
/** 差异放大面板左上角位置(客户区像素;<0 表示自动定位)。 */
double diffAmpPanelX = -1.0;
/** 弱波增强面板左上角位置(客户区像素;<0 表示自动定位)。 */
double weakBoostPanelX = -1.0;
/** 差异放大面板左上角纵坐标(<0 表示自动定位)。 */
double diffAmpPanelY = -1.0;
/** 弱波增强面板左上角纵坐标(<0 表示自动定位)。 */
double weakBoostPanelY = -1.0;
};
/**
@@ -570,45 +568,6 @@ namespace jdi
*/
void ComputeMovingAverage(const std::vector<double>& values, int radius, std::vector<float>& out);
/**
* @brief 计算 N 点滑动平均(float 输入版本)。
*
* 与 `ComputeMovingAverage(const std::vector<double>&, ...)` 完全同一实现,
* 供「差异放大」在 float 缓冲上二次滑动平均使用:亿级采样下若用 double 中转
* 会多占一倍内存,而曲线本身只要求 float 精度。
*
* @param [IN] values 原始采样值。
* @param [IN] radius 平滑半径 N(0 表示原样返回)。
* @param [OUT] out 输出数组(长度与 values 相同)。
*/
void ComputeMovingAverage(const std::vector<float>& values, int radius, std::vector<float>& out);
/**
* @brief 「差异放大」:把不明显的局部差异放大,而差异已经很大的区域逐点不变。
*
* 算法(三部分,全部 O(n)):
* 1. **趋势线** `trend` = 以 8×window 为半径的滑动平均 —— 把“整体”(低频成分)分离出来;
* 该半径必须远大于纹波周期,趋势线才不带着纹波。
* 2. **局部差异强度** `strength` = |采样值 − 趋势线| 的滑动平均(半径 = window)
* —— 衡量该处“纹波有多明显”。窗口恰好覆盖一个纹波周期时该值与相位无关
* (正弦为 2A/π),因此 `window` 应取**纹波周期的一半左右**;
* 3. **逐点增益** `gain = clamp(目标强度 / 该处强度, 1, 放大倍数)`,
* 目标强度取全信号差异强度的 75 分位(用直方图统计,O(1) 额外内存;
* 用均值会被个别尖峰抬高,用最大值则完全被最尖处主导)。
*
* 输出 `out = trend + gain × (采样值 − trend)`。
* **增益下界为 1**,所以差异强度不低于目标强度的采样点(即绝大多数“正常”数据点)
* 输出与原值逐位相同 —— 这正是“不影响其它数据点的振幅范围”的实现方式;
* 只有强度低于目标强度的区域(如被削弱的那个周期)才被放大,最多放大到 `gain` 倍。
*
* @param [IN] values 原始采样值。
* @param [IN] window 窗口点数 X(局部差异强度统计半径,应取纹波周期的一半左右)。
* @param [IN] gain 放大倍数 N(最大增益,至少 1)。
* @param [OUT] out 输出数组(长度与 values 相同;原值为 NaN 处保持 NaN)。
*/
void ComputeDiffAmplify(const std::vector<double>& values, int window, int gain,
std::vector<float>& out);
/**
* @brief 计算多阶(迭代)滑动平均:把 N 点滑动平均重复应用 `order` 次。
*
@@ -623,6 +582,32 @@ namespace jdi
void ComputeMovingAverageIterated(const std::vector<double>& values, int radius, int order,
std::vector<float>& out);
/**
* @brief 「弱波增强」:把比周边更弱的纹波波峰/波谷恢复(自适应弱波增强)。
*
* 思路(全部 O(n)):
* 1. **识别趋势与极值**:对轻度平滑后的信号扫描单调段,方向发生翻转处即为
* 波峰(先升后降)或波谷(先降后升);波峰与波谷交替出现。
* 2. **判定“弱”**:对第 k 个波峰,取同类的左右相邻波峰(k−2、k+2)。
* 若本波峰低于两者的均值,说明它是被削弱的那个;波谷反之(高于两者均值)。
* 3. **目标值**(`level` = 弱波增强等级,0–1):
* * 波峰:左右两个波峰中较小者为 0%、较大者为 100%,
* 目标 = min + level × (max − min);
* * 波谷:左右两个波谷中较大者为 0%、较小者为 100%,
* 目标 = max − level × (max − min)。
* 4. **平滑偏移**:极值点本身取其目标值,两侧的数据点按相邻两个极值点的偏移量
* 平滑过渡(平滑步进,两端导数为 0),使波形整体趋势不变、只在弱波附近被推回。
*
* 与“全局缩放/局部放大”不同,这里**只改动被判为弱波的极值及其两侧**,
* 正常周期的极值不满足条件、偏移量恒为 0,因此输出与输入逐位相同。
*
* @param [IN] values 原始采样值。
* @param [IN] level 弱波增强等级(0–1,超出范围会被夹取)。
* @param [OUT] out 输出数组(长度与 values 相同;原值为 NaN 处保持 NaN)。
*/
void ComputeWeakWaveBoost(const std::vector<double>& values, double level,
std::vector<float>& out);
/**
* @brief 在指定文件的时间轴上查找与给定时刻最接近的采样点。
* @param [IN] file 目标文件。