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

- 移除差异放大: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

+54 -74
View File
@@ -1461,71 +1461,67 @@ namespace jdi
return true;
}
bool Application::BuildDerivedSignal(int fileIndex, int signalIndex, int points, int order,
bool diffAmp, bool makeVisible)
bool Application::BuildDerivedSignal(const DerivedSignalRequest& request, bool makeVisible)
{
const DataFile* file = m_dataSet.FileAt(fileIndex);
if (file == nullptr || signalIndex < 0 || signalIndex >= static_cast<int>(file->signals.size()))
const DataFile* file = m_dataSet.FileAt(request.fileIndex);
if (file == nullptr || request.signalIndex < 0 ||
request.signalIndex >= static_cast<int>(file->signals.size()))
{
return false;
}
const SignalData& source = file->signals[static_cast<std::size_t>(signalIndex)];
const SignalData& source = file->signals[static_cast<std::size_t>(request.signalIndex)];
const std::wstring baseName = source.Title();
// 1. 计算派生采样值(与网格内的预览曲线共用同一实现,因此逐位一致)。
// 多阶均值滤波形如「<原信号名称> (N点均值×M阶)」,
// 弱波增强形如「<原信号名称> (弱波增强 M%)」。
std::vector<float> derived_values;
if (diffAmp)
{
ComputeDiffAmplify(source.values, points, order, derived_values);
}
else
{
ComputeMovingAverageIterated(source.values, points, order, derived_values);
}
// 2. 组装新信号:均值滤波形如「<原信号名称> (N点均值×M阶)」,
// 差异放大形如「<原信号名称> (X点差异放大×N倍)」。
SignalData derived;
if (diffAmp)
if (request.weakBoost)
{
derived.name = baseName + L" (" + std::to_wstring(points) + L"点差异放大×" +
std::to_wstring(order) + L"倍)";
ComputeWeakWaveBoost(source.values, static_cast<double>(request.levelPercent) / 100.0,
derived_values);
derived.name = baseName + L" (弱波增强 " + std::to_wstring(request.levelPercent) + L"%)";
}
else
{
derived.name = baseName + L" (" + std::to_wstring(points) + L"点均值";
if (order > 1)
ComputeMovingAverageIterated(source.values, request.points, request.order, derived_values);
derived.name = baseName + L" (" + std::to_wstring(request.points) + L"点均值";
if (request.order > 1)
{
derived.name += L"×" + std::to_wstring(order) + L"阶";
derived.name += L"×" + std::to_wstring(request.order) + L"阶";
}
derived.name += L")";
}
// 2. 组装新信号。
derived.rawHeader = derived.name;
derived.unit = source.unit;
derived.values.assign(derived_values.begin(), derived_values.end());
// 3. 插入到原信号的下方(数据层 + 各标签页引用重映射)。
int newIndex = -1;
if (!m_dataSet.InsertDerivedSignal(fileIndex, signalIndex, std::move(derived), newIndex))
if (!m_dataSet.InsertDerivedSignal(request.fileIndex, request.signalIndex, std::move(derived),
newIndex))
{
return false;
}
for (TabConfig& tab : m_tabs)
{
tab.RemapAfterSignalInsert(fileIndex, newIndex);
tab.RemapAfterSignalInsert(request.fileIndex, newIndex);
}
// 4. 在当前标签页中勾选新信号,并把它所在的网格移到原信号网格的下方。
if (makeVisible && !m_tabs.empty())
{
SignalRef derivedRef;
derivedRef.fileIndex = fileIndex;
derivedRef.fileIndex = request.fileIndex;
derivedRef.signalIndex = newIndex;
ActiveTab().SetVisible(derivedRef, true);
SignalRef sourceRef;
sourceRef.fileIndex = fileIndex;
sourceRef.signalIndex = signalIndex;
sourceRef.fileIndex = request.fileIndex;
sourceRef.signalIndex = request.signalIndex;
ActiveTab().MoveLaneOfSignalBelow(derivedRef, sourceRef);
}
@@ -1536,18 +1532,9 @@ namespace jdi
bool Application::ApplyPendingDerivedBuild()
{
// 两类「构建为新信号」请求(均值滤波 / 差异放大)机制完全一致,
// 两类「构建为新信号」请求(均值滤波 / 弱波增强)机制完全一致,
// 统一整理成同一份构建清单后按下标降序处理。
struct BuildRequest
{
int fileIndex = -1;
int signalIndex = -1;
int first = 0;
int second = 0;
bool diffAmp = false;
};
std::vector<BuildRequest> requests;
std::vector<DerivedSignalRequest> requests;
{
int laneIndex = -1;
std::vector<PlotView::MeanFilterBuild> items;
@@ -1555,29 +1542,28 @@ namespace jdi
{
for (const PlotView::MeanFilterBuild& item : items)
{
BuildRequest request;
DerivedSignalRequest request;
request.fileIndex = item.fileIndex;
request.signalIndex = item.signalIndex;
request.first = item.points;
request.second = item.order;
request.diffAmp = false;
request.points = item.points;
request.order = item.order;
request.weakBoost = false;
requests.push_back(request);
}
}
}
{
int laneIndex = -1;
std::vector<PlotView::DiffAmpBuild> items;
if (m_plot.TakePendingDiffAmpBuild(laneIndex, items))
std::vector<PlotView::WeakBoostBuild> items;
if (m_plot.TakePendingWeakBoostBuild(laneIndex, items))
{
for (const PlotView::DiffAmpBuild& item : items)
for (const PlotView::WeakBoostBuild& item : items)
{
BuildRequest request;
DerivedSignalRequest request;
request.fileIndex = item.fileIndex;
request.signalIndex = item.signalIndex;
request.first = item.points;
request.second = item.gain;
request.diffAmp = true;
request.levelPercent = item.levelPercent;
request.weakBoost = true;
requests.push_back(request);
}
}
@@ -1594,7 +1580,7 @@ namespace jdi
// **同一文件内必须按下标降序处理**:每次插入只让插入点之后的信号下标 +1,
// 降序处理可保证尚未处理的信号下标始终有效(升序会把新插入的信号当成原信号)。
std::sort(requests.begin(), requests.end(),
[](const BuildRequest& a, const BuildRequest& b)
[](const DerivedSignalRequest& a, const DerivedSignalRequest& b)
{
if (a.fileIndex != b.fileIndex)
{
@@ -1604,35 +1590,29 @@ namespace jdi
});
int built = 0;
int lastFirst = 0;
int lastSecond = 1;
bool lastDiffAmp = false;
for (const BuildRequest& request : requests)
DerivedSignalRequest last;
for (const DerivedSignalRequest& request : requests)
{
if (BuildDerivedSignal(request.fileIndex, request.signalIndex, request.first,
request.second, request.diffAmp, true))
if (BuildDerivedSignal(request, true))
{
++built;
lastFirst = request.first;
lastSecond = request.second;
lastDiffAmp = request.diffAmp;
last = request;
}
}
if (built > 0)
{
std::wstring text = L"已构建 " + std::to_wstring(built) + L" 条 ";
if (lastDiffAmp)
if (last.weakBoost)
{
text += std::to_wstring(lastFirst) + L"点差异放大×" +
std::to_wstring(lastSecond) + L"倍信号。";
text += L"弱波增强 " + std::to_wstring(last.levelPercent) + L"% 信号。";
}
else
{
text += std::to_wstring(lastFirst) + L"点均值";
if (lastSecond > 1)
text += std::to_wstring(last.points) + L"点均值";
if (last.order > 1)
{
text += L"×" + std::to_wstring(lastSecond) + L"阶";
text += L"×" + std::to_wstring(last.order) + L"阶";
}
text += L"滤波信号。";
}
@@ -1769,11 +1749,11 @@ namespace jdi
meanFilter.checked = m_plot.LaneMeanFilter(laneIndex);
experimental.submenu.push_back(meanFilter);
MenuItem diffAmp;
diffAmp.label = L"差异放大";
diffAmp.id = PLOT_CMD_LANE_DIFF_AMP;
diffAmp.checked = m_plot.LaneDiffAmp(laneIndex);
experimental.submenu.push_back(diffAmp);
MenuItem weakBoost;
weakBoost.label = L"弱波增强";
weakBoost.id = PLOT_CMD_LANE_WEAK_BOOST;
weakBoost.checked = m_plot.LaneWeakBoost(laneIndex);
experimental.submenu.push_back(weakBoost);
}
items.push_back(experimental);
@@ -1888,9 +1868,9 @@ namespace jdi
// 「实验性功能 → 均值滤波」:切换均值滤波曲线与参数面板。
m_plot.ToggleLaneMeanFilter(laneIndex);
break;
case PLOT_CMD_LANE_DIFF_AMP:
// 「实验性功能 → 差异放大」:切换差异放大曲线与参数面板。
m_plot.ToggleLaneDiffAmp(laneIndex);
case PLOT_CMD_LANE_WEAK_BOOST:
// 「实验性功能 → 弱波增强」:切换弱波增强曲线与参数面板。
m_plot.ToggleLaneWeakBoost(laneIndex);
break;
case PLOT_CMD_LANE_SPLIT:
// 「拆分当前网格」:信号按个拆成独立网格,新网格顺次插入其下方。
@@ -2390,7 +2370,7 @@ namespace jdi
{
// 绘图区内的点击也可能提交图例改名,需要立刻落到数据层。
ApplyPendingRename();
// 「均值滤波 → 构建为新信号」按钮:立即构建新信号并同步各视图。
// 「构建为新信号」按钮(均值滤波 / 弱波增强):立即构建新信号并同步各视图。
ApplyPendingDerivedBuild();
InvalidateRect(hwnd, nullptr, FALSE);
}