Compare commits
10
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7344bbe9c4 | ||
|
|
59a84ba126 | ||
|
|
14b60f5d9e | ||
|
|
ff3319b842 | ||
|
|
cb030573fa | ||
|
|
0afa6360ae | ||
|
|
a30a6fcf4d | ||
|
|
1ddbc37b78 | ||
|
|
da83dc2987 | ||
|
|
800c8b835b |
No files matched your search
@@ -241,8 +241,8 @@ namespace jdi
|
|||||||
/**
|
/**
|
||||||
* @brief 应用绘图区提交的「实验性功能 → 构建为新信号」请求。
|
* @brief 应用绘图区提交的「实验性功能 → 构建为新信号」请求。
|
||||||
*
|
*
|
||||||
* 为指定网格内的每条被勾选信号各构建一条派生信号(多次迭代均值滤波或弱波增强),
|
* 为指定网格内的每条被勾选信号各构建一条派生信号(多次迭代均值滤波或弱峰调整),
|
||||||
* 命名为 `<原信号名称> (N点均值…)` / `<原信号名称> (弱波增强 M%)`,
|
* 命名为 `<原信号名称> (N点均值…)` / `<原信号名称> (弱峰调整 M%)`,
|
||||||
* 插入到原信号所在组的原信号位置**下方**,并把新信号所在的网格移动到原信号网格的下方。
|
* 插入到原信号所在组的原信号位置**下方**,并把新信号所在的网格移动到原信号网格的下方。
|
||||||
*
|
*
|
||||||
* @return 实际构建了新信号返回 true。
|
* @return 实际构建了新信号返回 true。
|
||||||
@@ -250,7 +250,7 @@ namespace jdi
|
|||||||
bool ApplyPendingDerivedBuild();
|
bool ApplyPendingDerivedBuild();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 派生信号构建请求(多次迭代均值滤波 / 弱波增强共用同一套插入流程)。
|
* @brief 派生信号构建请求(多次迭代均值滤波 / 弱峰调整共用同一套插入流程)。
|
||||||
*/
|
*/
|
||||||
struct DerivedSignalRequest
|
struct DerivedSignalRequest
|
||||||
{
|
{
|
||||||
@@ -258,12 +258,12 @@ namespace jdi
|
|||||||
int signalIndex = -1; /**< 原信号下标。 */
|
int signalIndex = -1; /**< 原信号下标。 */
|
||||||
int points = 5; /**< 多次迭代均值滤波:均值点数 X(滑平半径)。 */
|
int points = 5; /**< 多次迭代均值滤波:均值点数 X(滑平半径)。 */
|
||||||
int iterations = 1; /**< 多次迭代均值滤波:迭代次数 N(至少 1)。 */
|
int iterations = 1; /**< 多次迭代均值滤波:迭代次数 N(至少 1)。 */
|
||||||
int levelPercent = 50; /**< 弱波增强:增强等级(0–100)。 */
|
int amountPercent = 0; /**< 弱峰调整:调整幅度(-400–400,默认 0)。 */
|
||||||
bool weakBoost = false; /**< true = 弱波增强,false = 多次迭代均值滤波。 */
|
bool weakPeak = false; /**< true = 弱峰调整,false = 多次迭代均值滤波。 */
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 把一条派生信号(多次迭代均值滤波 / 弱波增强)插入数据层并同步各视图。
|
* @brief 把一条派生信号(多次迭代均值滤波 / 弱峰调整)插入数据层并同步各视图。
|
||||||
* @param [IN] request 构建请求。
|
* @param [IN] request 构建请求。
|
||||||
* @param [IN] makeVisible 是否在当前标签页中勾选新信号。
|
* @param [IN] makeVisible 是否在当前标签页中勾选新信号。
|
||||||
* @return 构建并插入成功返回 true。
|
* @return 构建并插入成功返回 true。
|
||||||
|
|||||||
@@ -1481,14 +1481,14 @@ namespace jdi
|
|||||||
|
|
||||||
// 1. 计算派生采样值(与网格内的预览曲线共用同一实现,因此逐位一致)。
|
// 1. 计算派生采样值(与网格内的预览曲线共用同一实现,因此逐位一致)。
|
||||||
// 多次迭代均值滤波形如「<原信号名称> (N点均值 M次迭代)」,
|
// 多次迭代均值滤波形如「<原信号名称> (N点均值 M次迭代)」,
|
||||||
// 弱波增强形如「<原信号名称> (弱波增强 M% N次迭代)」。
|
// 弱峰调整形如「<原信号名称> (弱峰调整 M% N次迭代)」。
|
||||||
std::vector<float> derived_values;
|
std::vector<float> derived_values;
|
||||||
SignalData derived;
|
SignalData derived;
|
||||||
if (request.weakBoost)
|
if (request.weakPeak)
|
||||||
{
|
{
|
||||||
ComputeWeakWaveBoost(source.values, static_cast<double>(request.levelPercent) / 100.0,
|
ComputeWeakPeakAdjust(source.values, static_cast<double>(request.amountPercent) / 100.0,
|
||||||
request.iterations, derived_values);
|
request.iterations, derived_values);
|
||||||
derived.name = baseName + L" (弱波增强 " + std::to_wstring(request.levelPercent) + L"% " +
|
derived.name = baseName + L" (弱峰调整 " + std::to_wstring(request.amountPercent) + L"% " +
|
||||||
std::to_wstring(request.iterations) + L"次迭代)";
|
std::to_wstring(request.iterations) + L"次迭代)";
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -1538,7 +1538,7 @@ namespace jdi
|
|||||||
|
|
||||||
bool Application::ApplyPendingDerivedBuild()
|
bool Application::ApplyPendingDerivedBuild()
|
||||||
{
|
{
|
||||||
// 两类「构建为新信号」请求(均值滤波 / 弱波增强)机制完全一致,
|
// 两类「构建为新信号」请求(均值滤波 / 弱峰调整)机制完全一致,
|
||||||
// 统一整理成同一份构建清单后按下标降序处理。
|
// 统一整理成同一份构建清单后按下标降序处理。
|
||||||
std::vector<DerivedSignalRequest> requests;
|
std::vector<DerivedSignalRequest> requests;
|
||||||
{
|
{
|
||||||
@@ -1553,24 +1553,24 @@ namespace jdi
|
|||||||
request.signalIndex = item.signalIndex;
|
request.signalIndex = item.signalIndex;
|
||||||
request.points = item.points;
|
request.points = item.points;
|
||||||
request.iterations = item.iterations;
|
request.iterations = item.iterations;
|
||||||
request.weakBoost = false;
|
request.weakPeak = false;
|
||||||
requests.push_back(request);
|
requests.push_back(request);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
int laneIndex = -1;
|
int laneIndex = -1;
|
||||||
std::vector<PlotView::WeakBoostBuild> items;
|
std::vector<PlotView::WeakPeakBuild> items;
|
||||||
if (m_plot.TakePendingWeakBoostBuild(laneIndex, items))
|
if (m_plot.TakePendingWeakPeakBuild(laneIndex, items))
|
||||||
{
|
{
|
||||||
for (const PlotView::WeakBoostBuild& item : items)
|
for (const PlotView::WeakPeakBuild& item : items)
|
||||||
{
|
{
|
||||||
DerivedSignalRequest request;
|
DerivedSignalRequest request;
|
||||||
request.fileIndex = item.fileIndex;
|
request.fileIndex = item.fileIndex;
|
||||||
request.signalIndex = item.signalIndex;
|
request.signalIndex = item.signalIndex;
|
||||||
request.levelPercent = item.levelPercent;
|
request.amountPercent = item.amountPercent;
|
||||||
request.iterations = item.iterations;
|
request.iterations = item.iterations;
|
||||||
request.weakBoost = true;
|
request.weakPeak = true;
|
||||||
requests.push_back(request);
|
requests.push_back(request);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1610,9 +1610,9 @@ namespace jdi
|
|||||||
if (built > 0)
|
if (built > 0)
|
||||||
{
|
{
|
||||||
std::wstring text = L"已构建 " + std::to_wstring(built) + L" 条 ";
|
std::wstring text = L"已构建 " + std::to_wstring(built) + L" 条 ";
|
||||||
if (last.weakBoost)
|
if (last.weakPeak)
|
||||||
{
|
{
|
||||||
text += L"弱波增强 " + std::to_wstring(last.levelPercent) + L"% " +
|
text += L"弱峰调整 " + std::to_wstring(last.amountPercent) + L"% " +
|
||||||
std::to_wstring(last.iterations) + L"次迭代信号。";
|
std::to_wstring(last.iterations) + L"次迭代信号。";
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -1753,11 +1753,11 @@ namespace jdi
|
|||||||
meanFilter.checked = m_plot.LaneMeanFilter(laneIndex);
|
meanFilter.checked = m_plot.LaneMeanFilter(laneIndex);
|
||||||
experimental.submenu.push_back(meanFilter);
|
experimental.submenu.push_back(meanFilter);
|
||||||
|
|
||||||
MenuItem weakBoost;
|
MenuItem weakPeak;
|
||||||
weakBoost.label = L"弱波增强";
|
weakPeak.label = L"弱峰调整";
|
||||||
weakBoost.id = PLOT_CMD_LANE_WEAK_BOOST;
|
weakPeak.id = PLOT_CMD_LANE_WEAK_PEAK;
|
||||||
weakBoost.checked = m_plot.LaneWeakBoost(laneIndex);
|
weakPeak.checked = m_plot.LaneWeakPeak(laneIndex);
|
||||||
experimental.submenu.push_back(weakBoost);
|
experimental.submenu.push_back(weakPeak);
|
||||||
}
|
}
|
||||||
items.push_back(experimental);
|
items.push_back(experimental);
|
||||||
|
|
||||||
@@ -1872,9 +1872,9 @@ namespace jdi
|
|||||||
// 「实验性功能 → 均值滤波」:切换均值滤波曲线与参数面板。
|
// 「实验性功能 → 均值滤波」:切换均值滤波曲线与参数面板。
|
||||||
m_plot.ToggleLaneMeanFilter(laneIndex);
|
m_plot.ToggleLaneMeanFilter(laneIndex);
|
||||||
break;
|
break;
|
||||||
case PLOT_CMD_LANE_WEAK_BOOST:
|
case PLOT_CMD_LANE_WEAK_PEAK:
|
||||||
// 「实验性功能 → 弱波增强」:切换弱波增强曲线与参数面板。
|
// 「实验性功能 → 弱峰调整」:切换弱峰调整曲线与参数面板。
|
||||||
m_plot.ToggleLaneWeakBoost(laneIndex);
|
m_plot.ToggleLaneWeakPeak(laneIndex);
|
||||||
break;
|
break;
|
||||||
case PLOT_CMD_LANE_SPLIT:
|
case PLOT_CMD_LANE_SPLIT:
|
||||||
// 「拆分当前网格」:信号按个拆成独立网格,新网格顺次插入其下方。
|
// 「拆分当前网格」:信号按个拆成独立网格,新网格顺次插入其下方。
|
||||||
@@ -2378,7 +2378,7 @@ namespace jdi
|
|||||||
{
|
{
|
||||||
// 绘图区内的点击也可能提交图例改名,需要立刻落到数据层。
|
// 绘图区内的点击也可能提交图例改名,需要立刻落到数据层。
|
||||||
ApplyPendingRename();
|
ApplyPendingRename();
|
||||||
// 「构建为新信号」按钮(均值滤波 / 弱波增强):立即构建新信号并同步各视图。
|
// 「构建为新信号」按钮(均值滤波 / 弱峰调整):立即构建新信号并同步各视图。
|
||||||
ApplyPendingDerivedBuild();
|
ApplyPendingDerivedBuild();
|
||||||
InvalidateRect(hwnd, nullptr, FALSE);
|
InvalidateRect(hwnd, nullptr, FALSE);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,8 +43,8 @@ namespace jdi
|
|||||||
MOVE_SIGNAL, /**< 拖拽信号曲线到其它网格。 */
|
MOVE_SIGNAL, /**< 拖拽信号曲线到其它网格。 */
|
||||||
EDGE_MOVE, /**< 拖动「边沿计数」的横向光标(上下移动幅值)。 */
|
EDGE_MOVE, /**< 拖动「边沿计数」的横向光标(上下移动幅值)。 */
|
||||||
EDGE_PANEL_MOVE, /**< 拖动边沿计数浮动面板。 */
|
EDGE_PANEL_MOVE, /**< 拖动边沿计数浮动面板。 */
|
||||||
BOOST_LEVEL, /**< 拖动「弱波增强」面板中的等级滑块。 */
|
PEAK_LEVEL, /**< 拖动「弱峰调整」某信号的幅度滑块(-400% – +400%)。 */
|
||||||
BOOST_ITERATIONS, /**< 拖动「弱波增强」面板中的迭代次数 N 滑块。 */
|
PEAK_ITERATIONS, /**< 拖动「弱峰调整」某信号的迭代次数滑块(1 … 该信号自定义上限)。 */
|
||||||
LANE_REORDER /**< 拖动网格图例中的 GX 编号徽标:上下调整网格次序。 */
|
LANE_REORDER /**< 拖动网格图例中的 GX 编号徽标:上下调整网格次序。 */
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -72,7 +72,8 @@ namespace jdi
|
|||||||
EDGE_CENTER_POINTS, /**< 编辑「中心轴边沿计数」的均值点数 X。 */
|
EDGE_CENTER_POINTS, /**< 编辑「中心轴边沿计数」的均值点数 X。 */
|
||||||
EDGE_CENTER_ITERATIONS, /**< 编辑「中心轴边沿计数」的迭代次数 N。 */
|
EDGE_CENTER_ITERATIONS, /**< 编辑「中心轴边沿计数」的迭代次数 N。 */
|
||||||
MEAN_FILTER_POINTS, /**< 编辑「均值滤波」的均值点数 X。 */
|
MEAN_FILTER_POINTS, /**< 编辑「均值滤波」的均值点数 X。 */
|
||||||
MEAN_FILTER_ITERATIONS /**< 编辑「均值滤波」的迭代次数 N。 */
|
MEAN_FILTER_ITERATIONS, /**< 编辑「均值滤波」的迭代次数 N。 */
|
||||||
|
PEAK_ITERATIONS_MAX /**< 编辑「弱峰调整」某信号「迭代次数 N」滑块的**上限**(正整数)。 */
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -90,7 +91,7 @@ namespace jdi
|
|||||||
PLOT_CMD_LANE_EDGE_CENTER = 8, /**< 切换本网格的「实验性功能 → 边沿计数(中心轴)」开关。 */
|
PLOT_CMD_LANE_EDGE_CENTER = 8, /**< 切换本网格的「实验性功能 → 边沿计数(中心轴)」开关。 */
|
||||||
PLOT_CMD_LANE_SPLIT = 9, /**< 拆分当前网格:把网格内信号按个拆成独立网格并顺次下移编号。 */
|
PLOT_CMD_LANE_SPLIT = 9, /**< 拆分当前网格:把网格内信号按个拆成独立网格并顺次下移编号。 */
|
||||||
PLOT_CMD_LANE_MEAN_FILTER = 10, /**< 切换本网格的「实验性功能 → 均值滤波」开关。 */
|
PLOT_CMD_LANE_MEAN_FILTER = 10, /**< 切换本网格的「实验性功能 → 均值滤波」开关。 */
|
||||||
PLOT_CMD_LANE_WEAK_BOOST = 11 /**< 切换本网格的「实验性功能 → 弱波增强」开关。 */
|
PLOT_CMD_LANE_WEAK_PEAK = 11 /**< 切换本网格的「实验性功能 → 弱峰调整」开关。 */
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -101,7 +102,7 @@ namespace jdi
|
|||||||
COUNT = 0, /**< 边沿计数。 */
|
COUNT = 0, /**< 边沿计数。 */
|
||||||
CENTER = 1, /**< 边沿计数(中心轴)。 */
|
CENTER = 1, /**< 边沿计数(中心轴)。 */
|
||||||
MEAN = 2, /**< 均值滤波。 */
|
MEAN = 2, /**< 均值滤波。 */
|
||||||
BOOST = 3 /**< 弱波增强。 */
|
PEAK = 3 /**< 弱峰调整。 */
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -112,7 +113,7 @@ namespace jdi
|
|||||||
enum class DerivedCurveKind : int
|
enum class DerivedCurveKind : int
|
||||||
{
|
{
|
||||||
MEAN_FILTER = 0, /**< 多次迭代均值滤波。 */
|
MEAN_FILTER = 0, /**< 多次迭代均值滤波。 */
|
||||||
WEAK_BOOST = 1 /**< 弱波增强。 */
|
WEAK_PEAK = 1 /**< 弱峰调整。 */
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -138,11 +139,11 @@ namespace jdi
|
|||||||
void SetHostWindow(HWND host) { m_host = host; }
|
void SetHostWindow(HWND host) { m_host = host; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 后台「网格图层」绘制线程已完成的图层数量(仅供验证 / 诊断)。
|
* @brief 已渲染的「信号图层」区域数量(仅供验证 / 诊断)。
|
||||||
*
|
*
|
||||||
* 计数 > 0 即说明单网格基底的绘制确实发生在**后台线程**,
|
* 每次把一个网格的基底(整块或滚动细条)画进图层都会 +1。计数长期不增长即说明
|
||||||
* 前台只负责位图搬运(贴图)与叠加层。
|
* 平移期间确实在复用已有像素(只做坐标偏移),没有重新绘制波形。
|
||||||
* @return 累计完成的图层数量。
|
* @return 累计完成的图层区域数量。
|
||||||
*/
|
*/
|
||||||
std::uint64_t BackgroundLayerRenderCount() const
|
std::uint64_t BackgroundLayerRenderCount() const
|
||||||
{
|
{
|
||||||
@@ -569,115 +570,85 @@ namespace jdi
|
|||||||
bool TakePendingMeanFilterBuild(int& laneIndex, std::vector<MeanFilterBuild>& items);
|
bool TakePendingMeanFilterBuild(int& laneIndex, std::vector<MeanFilterBuild>& items);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 「实验性功能 → 弱波增强」的「构建为新信号」单个请求
|
* @brief 「实验性功能 → 弱峰调整」的「构建为新信号」单个请求
|
||||||
* (一个被勾选的信号 + 整个网格共用的增强等级)。
|
* (一个被勾选的信号 + **它自己的**增强等级与迭代次数,与均值滤波同构)。
|
||||||
*/
|
*/
|
||||||
struct WeakBoostBuild
|
struct WeakPeakBuild
|
||||||
{
|
{
|
||||||
int fileIndex = -1; /**< 信号所属文件下标。 */
|
int fileIndex = -1; /**< 信号所属文件下标。 */
|
||||||
int signalIndex = -1; /**< 文件内信号下标。 */
|
int signalIndex = -1; /**< 文件内信号下标。 */
|
||||||
int levelPercent = 50; /**< 弱波增强等级(0–100)。 */
|
int amountPercent = 0; /**< 弱峰调整幅度(-400–400,默认 0 = 不改动)。 */
|
||||||
int iterations = 1; /**< 迭代次数 N(1–10)。 */
|
int iterations = 1; /**< 迭代次数 N(1–10)。 */
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 取出「弱波增强 → 构建为新信号」请求(由面板底部按钮触发)。
|
* @brief 取出「弱峰调整 → 构建为新信号」请求(由面板底部按钮触发)。
|
||||||
* @param [OUT] laneIndex 触发该请求的网格下标。
|
* @param [OUT] laneIndex 触发该请求的网格下标。
|
||||||
* @param [OUT] items 需要构建的信号及其参数(按信号在网格内的顺序)。
|
* @param [OUT] items 需要构建的信号及其参数(按信号在网格内的顺序)。
|
||||||
* @return 存在待处理请求返回 true(取出后清空)。
|
* @return 存在待处理请求返回 true(取出后清空)。
|
||||||
*/
|
*/
|
||||||
bool TakePendingWeakBoostBuild(int& laneIndex, std::vector<WeakBoostBuild>& items);
|
bool TakePendingWeakPeakBuild(int& laneIndex, std::vector<WeakPeakBuild>& items);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 切换某网格的「弱波增强」开关。
|
* @brief 切换某网格的「弱峰调整」开关。
|
||||||
* @param [IN] laneIndex 网格下标。
|
* @param [IN] laneIndex 网格下标。
|
||||||
* @param [IN] enabled 是否开启。
|
* @param [IN] enabled 是否开启。
|
||||||
* @return 状态发生变化返回 true。
|
* @return 状态发生变化返回 true。
|
||||||
*/
|
*/
|
||||||
bool SetLaneWeakBoost(int laneIndex, bool enabled);
|
bool SetLaneWeakPeak(int laneIndex, bool enabled);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 查询某网格的「弱波增强」开关。
|
* @brief 查询某网格的「弱峰调整」开关。
|
||||||
* @param [IN] laneIndex 网格下标。
|
* @param [IN] laneIndex 网格下标。
|
||||||
* @return 已开启返回 true。
|
* @return 已开启返回 true。
|
||||||
*/
|
*/
|
||||||
bool LaneWeakBoost(int laneIndex) const;
|
bool LaneWeakPeak(int laneIndex) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 切换某网格的「弱波增强」开关。
|
* @brief 切换某网格的「弱峰调整」开关。
|
||||||
* @param [IN] laneIndex 网格下标。
|
* @param [IN] laneIndex 网格下标。
|
||||||
* @return 切换后的状态。
|
* @return 切换后的状态。
|
||||||
*/
|
*/
|
||||||
bool ToggleLaneWeakBoost(int laneIndex);
|
bool ToggleLaneWeakPeak(int laneIndex);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 读取某网格的弱波增强等级(0–1)。
|
* @brief 获取弱峰调整曲线(按「信号 + 增强等级 + 迭代次数」缓存)。
|
||||||
* @param [IN] laneIndex 网格下标。
|
|
||||||
* @return 增强等级;网格不存在时返回 0.5。
|
|
||||||
*/
|
|
||||||
double LaneWeakBoostLevel(int laneIndex) const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 读取某网格的弱波增强迭代次数 N(1–10)。
|
|
||||||
* @param [IN] laneIndex 网格下标。
|
|
||||||
* @return 迭代次数;网格不存在时返回 1。
|
|
||||||
*/
|
|
||||||
int LaneWeakBoostIterations(int laneIndex) const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 设置某网格的弱波增强迭代次数 N(1–10,超出范围会被夹取)。
|
|
||||||
* @param [IN] laneIndex 网格下标。
|
|
||||||
* @param [IN] iterations 迭代次数。
|
|
||||||
* @return 迭代次数发生变化返回 true。
|
|
||||||
*/
|
|
||||||
bool SetLaneWeakBoostIterations(int laneIndex, int iterations);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 设置某网格的弱波增强等级(0–1,超出范围会被夹取)。
|
|
||||||
* @param [IN] laneIndex 网格下标。
|
|
||||||
* @param [IN] level 增强等级。
|
|
||||||
* @return 等级发生变化返回 true。
|
|
||||||
*/
|
|
||||||
bool SetLaneWeakBoostLevel(int laneIndex, double level);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 获取弱波增强曲线(按「信号 + 增强等级 + 迭代次数」缓存)。
|
|
||||||
* @param [IN] signal 目标信号。
|
* @param [IN] signal 目标信号。
|
||||||
* @param [IN] levelPercent 弱波增强等级(0–100,整数百分比)。
|
* @param [IN] amountPercent 弱峰调整幅度(-400–400,整数百分比)。
|
||||||
* @param [IN] iterations 迭代次数 N(1–10)。
|
* @param [IN] iterations 迭代次数 N(1–10)。
|
||||||
* @return 曲线数值数组(长度与 `signal.values` 相同;缺失处为 NaN)。
|
* @return 曲线数值数组(长度与 `signal.values` 相同;缺失处为 NaN)。
|
||||||
*/
|
*/
|
||||||
const std::vector<float>& WeakBoostCurveFor(const SignalData& signal, int levelPercent,
|
const std::vector<float>& WeakPeakCurveFor(const SignalData& signal, int amountPercent,
|
||||||
int iterations) const;
|
int iterations) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 取某个时刻的弱波增强曲线取值(用于纵向光标的取值标注)。
|
* @brief 取某个时刻的弱峰调整曲线取值(用于纵向光标的取值标注)。
|
||||||
* @param [IN] file 信号所属文件。
|
* @param [IN] file 信号所属文件。
|
||||||
* @param [IN] signal 目标信号。
|
* @param [IN] signal 目标信号。
|
||||||
* @param [IN] levelPercent 弱波增强等级(0–100)。
|
* @param [IN] amountPercent 弱峰调整幅度(-400–400)。
|
||||||
* @param [IN] iterations 迭代次数 N(1–10)。
|
* @param [IN] iterations 迭代次数 N(1–10)。
|
||||||
* @param [IN] time 采样时刻。
|
* @param [IN] time 采样时刻。
|
||||||
* @param [OUT] value 输出取值。
|
* @param [OUT] value 输出取值。
|
||||||
* @return 取到有效值返回 true。
|
* @return 取到有效值返回 true。
|
||||||
*/
|
*/
|
||||||
bool WeakBoostValueAt(const DataFile& file, const SignalData& signal, int levelPercent, int iterations,
|
bool WeakPeakValueAt(const DataFile& file, const SignalData& signal, int amountPercent, int iterations,
|
||||||
double time, double& value) const;
|
double time, double& value) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 切换某个信号在「弱波增强」面板中的预览勾选状态。
|
* @brief 切换某个信号在「弱峰调整」面板中的预览勾选状态。
|
||||||
* @param [IN] laneIndex 网格下标。
|
* @param [IN] laneIndex 网格下标。
|
||||||
* @param [IN] ref 目标信号。
|
* @param [IN] ref 目标信号。
|
||||||
* @return 切换后的勾选状态。
|
* @return 切换后的勾选状态。
|
||||||
*/
|
*/
|
||||||
bool ToggleLaneWeakBoostSignal(int laneIndex, const SignalRef& ref);
|
bool ToggleLaneWeakPeakSignal(int laneIndex, const SignalRef& ref);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 查询某个信号在「弱波增强」面板中的预览勾选状态。
|
* @brief 查询某个信号在「弱峰调整」面板中的预览勾选状态。
|
||||||
* @param [IN] laneIndex 网格下标。
|
* @param [IN] laneIndex 网格下标。
|
||||||
* @param [IN] ref 目标信号。
|
* @param [IN] ref 目标信号。
|
||||||
* @return 已勾选返回 true。
|
* @return 已勾选返回 true。
|
||||||
*/
|
*/
|
||||||
bool LaneWeakBoostSignalEnabled(int laneIndex, const SignalRef& ref) const;
|
bool LaneWeakPeakSignalEnabled(int laneIndex, const SignalRef& ref) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 直接把文本解析为网格 Y 轴上限/下限并应用(供菜单内联输入框提交使用)。
|
* @brief 直接把文本解析为网格 Y 轴上限/下限并应用(供菜单内联输入框提交使用)。
|
||||||
@@ -946,13 +917,25 @@ namespace jdi
|
|||||||
/**
|
/**
|
||||||
* @brief 将两条光标的时间夹取到当前可见时间范围内。
|
* @brief 将两条光标的时间夹取到当前可见时间范围内。
|
||||||
*
|
*
|
||||||
* ⚠️ 现在**只在拖拽光标时**使用。视图变化(平移/放大/自适应)必须**不**移动光标:
|
* ⚠️ 拖拽光标时使用;**平移**视界后也要用(见 `ClampCursorsAfterPan`)。
|
||||||
|
* 而**离散**的视图变化(选区放大 / 适应视图 / 按网格时间轴展开)必须**不**移动光标:
|
||||||
* 光标允许滞留在网格可视区域之外,此时仅把它的取值/时间读数贴在网格边缘显示。
|
* 光标允许滞留在网格可视区域之外,此时仅把它的取值/时间读数贴在网格边缘显示。
|
||||||
*
|
*
|
||||||
* @param [IN] which 要夹取的光标序号(1 或 2)。
|
* @param [IN] which 要夹取的光标序号(1 或 2)。
|
||||||
*/
|
*/
|
||||||
void ClampCursorToView(int which);
|
void ClampCursorToView(int which);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief **平移视界**之后把时间光标夹回可见区间(单/双光标都处理)。
|
||||||
|
*
|
||||||
|
* ⚠️ 与「离散视图变化不移动光标」并不矛盾:选区放大 / 适应视图 / 按网格时间轴展开
|
||||||
|
* 仍然不动光标(用户可以先量好再自由探索,光标读数贴在边缘可读、可拖回来);
|
||||||
|
* 但**平移**是连续手势 —— 尤其在深放大区间,光标一旦被落在屏外就会立刻"跑飞"
|
||||||
|
* 很远(一屏的平移就是几十甚至上百倍可视跨度),用户会以为光标丢失。
|
||||||
|
* 因此在平移(中键/左键拖拽、Shift+滚轮、方向键)之后夹一次。
|
||||||
|
*/
|
||||||
|
void ClampCursorsAfterPan();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 计算双光标之间的时间差(绝对值)。
|
* @brief 计算双光标之间的时间差(绝对值)。
|
||||||
* @return 时间差;非双光标模式返回 0。
|
* @return 时间差;非双光标模式返回 0。
|
||||||
@@ -1089,7 +1072,7 @@ namespace jdi
|
|||||||
const std::vector<double>& timeTicks);
|
const std::vector<double>& timeTicks);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 绘制某网格的全部**派生预览曲线**(均值滤波 / 中心轴 / 弱波增强),叠加在原信号之上。
|
* @brief 绘制某网格的全部**派生预览曲线**(均值滤波 / 中心轴 / 弱峰调整),叠加在原信号之上。
|
||||||
*
|
*
|
||||||
* 从 `PaintLane` 抽出,供「宽缓冲区」前台路径复用:宽缓冲只存"基底"(网格/波形),
|
* 从 `PaintLane` 抽出,供「宽缓冲区」前台路径复用:宽缓冲只存"基底"(网格/波形),
|
||||||
* 派生曲线每帧作为叠加层单独绘制,因此拖派生参数滑块时无需重画波形。
|
* 派生曲线每帧作为叠加层单独绘制,因此拖派生参数滑块时无需重画波形。
|
||||||
@@ -1158,21 +1141,31 @@ namespace jdi
|
|||||||
gd::RectF close; /**< ✕ 关闭按钮。 */
|
gd::RectF close; /**< ✕ 关闭按钮。 */
|
||||||
gd::RectF edit; /**< 面板底部编辑行(无编辑内容时宽度为 0)。 */
|
gd::RectF edit; /**< 面板底部编辑行(无编辑内容时宽度为 0)。 */
|
||||||
gd::RectF button; /**< 面板底部按钮(无按钮时宽度为 0)。 */
|
gd::RectF button; /**< 面板底部按钮(无按钮时宽度为 0)。 */
|
||||||
gd::RectF slider; /**< 「弱波增强」等级滑块的轨道矩形(无滑块时宽度为 0)。 */
|
|
||||||
gd::RectF sliderIterations; /**< 「弱波增强」迭代次数 N 滑块的轨道矩形(无滑块时宽度为 0)。 */
|
|
||||||
int rows = 0; /**< 统计行数(每条可见信号一行)。 */
|
int rows = 0; /**< 统计行数(每条可见信号一行)。 */
|
||||||
/**
|
/**
|
||||||
* @brief 「逐条目」面板的矩形(MEAN 与 CENTER 面板共用同一套排布)。
|
* @brief 「逐条目」面板的矩形(MEAN / CENTER / PEAK 面板共用同一套排布)。
|
||||||
*
|
*
|
||||||
* 索引与 `rowRefs` 一一对应(即网格内可见信号的顺序):
|
* 索引与 `rowRefs` 一一对应(即网格内可见信号的顺序):
|
||||||
* `rowChecks` 为第一排右侧的复选框;`rowPoints` / `rowIterations` 为第二排的两个输入框;
|
* `rowChecks` 为第一排右侧的复选框;`rowPoints` / `rowIterations` 为**输入框**
|
||||||
* `rowStats` 为第三排的统计结果(中心轴边沿计数专用)。
|
*(MEAN / CENTER 使用);`rowStats` 为第三排的统计结果(中心轴边沿计数专用)。
|
||||||
* 未勾选的条目对应的矩形均为空。
|
* 未勾选的条目对应的矩形均为空。
|
||||||
*/
|
*/
|
||||||
std::vector<gd::RectF> rowChecks;
|
std::vector<gd::RectF> rowChecks;
|
||||||
std::vector<gd::RectF> rowPoints;
|
std::vector<gd::RectF> rowPoints;
|
||||||
std::vector<gd::RectF> rowIterations;
|
std::vector<gd::RectF> rowIterations;
|
||||||
std::vector<gd::RectF> rowStats;
|
std::vector<gd::RectF> rowStats;
|
||||||
|
/**
|
||||||
|
* @brief 「弱峰调整」每个被勾选信号自己的两行**滑块**(与 `rowRefs` 同序)。
|
||||||
|
*
|
||||||
|
* `rowLevelTracks/rowLevelValues` = 增强等级滑块与它右侧的数值文本;
|
||||||
|
* `rowIterTracks/rowIterValues` = 迭代次数滑块与它右侧的数值文本
|
||||||
|
*(**点值数值文本可以把滑块上限改为别的正整数**)。
|
||||||
|
* 未勾选或非 PEAK 面板时对应元素为空矩形(数组也可能为空)。
|
||||||
|
*/
|
||||||
|
std::vector<gd::RectF> rowLevelTracks;
|
||||||
|
std::vector<gd::RectF> rowLevelValues;
|
||||||
|
std::vector<gd::RectF> rowIterTracks;
|
||||||
|
std::vector<gd::RectF> rowIterValues;
|
||||||
std::vector<SignalRef> rowRefs;
|
std::vector<SignalRef> rowRefs;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1184,6 +1177,14 @@ namespace jdi
|
|||||||
*/
|
*/
|
||||||
EdgePanelLayout EdgePanelLayoutFor(int laneIndex, EdgePanelKind kind) const;
|
EdgePanelLayout EdgePanelLayoutFor(int laneIndex, EdgePanelKind kind) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 在面板布局里找出某个信号对应的「逐条目」行下标。
|
||||||
|
* @param [IN] layout 面板布局。
|
||||||
|
* @param [IN] ref 目标信号。
|
||||||
|
* @return 行下标(与 `rowRefs` 同序);未找到返回 -1。
|
||||||
|
*/
|
||||||
|
int PeakRowIndexOf(const EdgePanelLayout& layout, const SignalRef& ref) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 计算单个面板的缓存键(全局键之外,面板自身的内容)。
|
* @brief 计算单个面板的缓存键(全局键之外,面板自身的内容)。
|
||||||
* @param [IN] laneIndex 面板所属网格。
|
* @param [IN] laneIndex 面板所属网格。
|
||||||
@@ -1294,20 +1295,20 @@ namespace jdi
|
|||||||
* @brief 取得某个采样区间内的派生曲线(带缓存)。
|
* @brief 取得某个采样区间内的派生曲线(带缓存)。
|
||||||
*
|
*
|
||||||
* ⚠️ **性能关键**:派生曲线只用于“看得见的那一段”,而整条信号可能有数千万个采样点
|
* ⚠️ **性能关键**:派生曲线只用于“看得见的那一段”,而整条信号可能有数千万个采样点
|
||||||
* (VERY_BIG_DATA 为 2576 万)。若每次都重算整条曲线(实测弱波增强 ~800ms/条),
|
* (VERY_BIG_DATA 为 2576 万)。若每次都重算整条曲线(实测弱峰调整 ~800ms/条),
|
||||||
* 拖动等级/迭代次数滑块时每帧要花几秒钟,交互完全卡死。
|
* 拖动等级/迭代次数滑块时每帧要花几秒钟,交互完全卡死。
|
||||||
* 本函数只计算“所需区间 ± 保护带”,并按(信号 + 参数 + 对齐后的区间)缓存:
|
* 本函数只计算“所需区间 ± 保护带”,并按(信号 + 参数 + 对齐后的区间)缓存:
|
||||||
* 实测单条降到 1~3ms,滑块拖动与平移因此恢复流畅。
|
* 实测单条降到 1~3ms,滑块拖动与平移因此恢复流畅。
|
||||||
*
|
*
|
||||||
* 保护带保证可见区间内的取值与“整条曲线”一致:均值滤波的核是有限宽度,
|
* 保护带保证可见区间内的取值与“整条曲线”一致:均值滤波的核是有限宽度,
|
||||||
* 弱波增强的修正沿相邻极值向内传播(每迭代一次传一个极值),
|
* 弱峰调整的修正沿相邻极值向内传播(每迭代一次传一个极值),
|
||||||
* 因此只要求保护带足够宽(见 `kDerivedCurveGuard` 系列常量)。
|
* 因此只要求保护带足够宽(见 `kDerivedCurveGuard` 系列常量)。
|
||||||
*
|
*
|
||||||
* @param [IN] kind 曲线种类。
|
* @param [IN] kind 曲线种类。
|
||||||
* @param [IN] signal 目标信号。
|
* @param [IN] signal 目标信号。
|
||||||
* @param [IN] points 均值点数 X(仅 `MEAN_FILTER` 使用)。
|
* @param [IN] points 均值点数 X(仅 `MEAN_FILTER` 使用)。
|
||||||
* @param [IN] iterations 迭代次数 N。
|
* @param [IN] iterations 迭代次数 N。
|
||||||
* @param [IN] levelPercent 弱波增强等级(仅 `WEAK_BOOST` 使用,0–100)。
|
* @param [IN] amountPercent 弱峰调整幅度(仅 `WEAK_PEAK` 使用,-400–400)。
|
||||||
* @param [IN] begin 需要的采样区间起点(含)。
|
* @param [IN] begin 需要的采样区间起点(含)。
|
||||||
* @param [IN] end 需要的采样区间终点(不含)。
|
* @param [IN] end 需要的采样区间终点(不含)。
|
||||||
* @param [OUT] spanBegin 返回数组对应的首个采样下标。
|
* @param [OUT] spanBegin 返回数组对应的首个采样下标。
|
||||||
@@ -1318,7 +1319,7 @@ namespace jdi
|
|||||||
* @return 曲线数组指针(覆盖 `[spanBegin, spanBegin + size())`);暂无可显示曲线时返回 nullptr。
|
* @return 曲线数组指针(覆盖 `[spanBegin, spanBegin + size())`);暂无可显示曲线时返回 nullptr。
|
||||||
*/
|
*/
|
||||||
const std::vector<float>* CurveSpanFor(DerivedCurveKind kind, const SignalData& signal,
|
const std::vector<float>* CurveSpanFor(DerivedCurveKind kind, const SignalData& signal,
|
||||||
int points, int iterations, int levelPercent,
|
int points, int iterations, int amountPercent,
|
||||||
std::size_t begin, std::size_t end,
|
std::size_t begin, std::size_t end,
|
||||||
std::size_t& spanBegin,
|
std::size_t& spanBegin,
|
||||||
bool allowAsyncCompute = false) const;
|
bool allowAsyncCompute = false) const;
|
||||||
@@ -1336,12 +1337,12 @@ namespace jdi
|
|||||||
int kind = 0; /**< `DerivedCurveKind`。 */
|
int kind = 0; /**< `DerivedCurveKind`。 */
|
||||||
int points = 5; /**< 均值点数 X。 */
|
int points = 5; /**< 均值点数 X。 */
|
||||||
int iterations = 1; /**< 迭代次数 N。 */
|
int iterations = 1; /**< 迭代次数 N。 */
|
||||||
int levelPercent = 0; /**< 弱波增强等级(0–100)。 */
|
int amountPercent = 0; /**< 弱峰调整幅度(-400–400)。 */
|
||||||
|
|
||||||
bool operator==(const CurveKey& other) const
|
bool operator==(const CurveKey& other) const
|
||||||
{
|
{
|
||||||
return signal == other.signal && kind == other.kind && points == other.points &&
|
return signal == other.signal && kind == other.kind && points == other.points &&
|
||||||
iterations == other.iterations && levelPercent == other.levelPercent;
|
iterations == other.iterations && amountPercent == other.amountPercent;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1399,11 +1400,19 @@ namespace jdi
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 取得该曲线键下最新的已算好条目并钉住(双缓冲的「旧缓冲」)。
|
* @brief 取得该曲线键下最新的已算好条目并钉住(双缓冲的「旧缓冲」)。
|
||||||
* @param [IN] key 曲线键。
|
*
|
||||||
|
* ⚠️ **只按「信号 + 曲线种类」匹配,忽略参数**:拖参数滑块时每帧参数都在变,
|
||||||
|
* 若要求键完全相同就永远找不到上一帧的曲线,本帧便无曲线可画 → 预览曲线**闪烁**。
|
||||||
|
* 旧曲线只是参数略旧,按采样下标映射位置依然正确,先顶上再等后台算好新曲线。
|
||||||
|
*
|
||||||
|
* @param [IN] key 曲线键(只用其 `signal` 与 `kind`)。
|
||||||
|
* @param [IN] begin 需要区间起点(用于优先挑「覆盖该区间」的条目)。
|
||||||
|
* @param [IN] end 需要区间终点(不含)。
|
||||||
* @param [OUT] spanBegin 命中条目的窗口起点。
|
* @param [OUT] spanBegin 命中条目的窗口起点。
|
||||||
* @return 命中条目,无则返回 nullptr。
|
* @return 命中条目,无则返回 nullptr。
|
||||||
*/
|
*/
|
||||||
const CurveEntry* AcquireNewestCurve(const CurveKey& key, std::size_t& spanBegin) const;
|
const CurveEntry* AcquireNewestCurve(const CurveKey& key, std::size_t begin, std::size_t end,
|
||||||
|
std::size_t& spanBegin) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 发布一条新算好的曲线窗口并钉住。
|
* @brief 发布一条新算好的曲线窗口并钉住。
|
||||||
@@ -1461,15 +1470,15 @@ namespace jdi
|
|||||||
int points, int iterations) const;
|
int points, int iterations) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 绘制某条信号的弱波增强曲线(与均值滤波曲线同一配色派生规则)。
|
* @brief 绘制某条信号的弱峰调整曲线(与均值滤波曲线同一配色派生规则)。
|
||||||
* @param [IN,OUT] g 绘图上下文。
|
* @param [IN,OUT] g 绘图上下文。
|
||||||
* @param [IN] resolved 信号视图。
|
* @param [IN] resolved 信号视图。
|
||||||
* @param [IN] lane 网格绘制信息。
|
* @param [IN] lane 网格绘制信息。
|
||||||
* @param [IN] levelPercent 弱波增强等级(0–100)。
|
* @param [IN] amountPercent 弱峰调整幅度(-400–400)。
|
||||||
* @param [IN] iterations 迭代次数 N(1–10)。
|
* @param [IN] iterations 迭代次数 N(1–10)。
|
||||||
*/
|
*/
|
||||||
void PaintWeakBoostCurve(gd::Graphics& g, const ResolvedSignal& resolved, const LaneVisual& lane,
|
void PaintWeakPeakCurve(gd::Graphics& g, const ResolvedSignal& resolved, const LaneVisual& lane,
|
||||||
int levelPercent, int iterations) const;
|
int amountPercent, int iterations) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 数据点标记的位置与"越界方向"(用于决定画圆点还是贴边三角)。
|
* @brief 数据点标记的位置与"越界方向"(用于决定画圆点还是贴边三角)。
|
||||||
@@ -1685,11 +1694,18 @@ namespace jdi
|
|||||||
bool m_pendingRename = false; /**< 是否存在待应用的重命名请求。 */
|
bool m_pendingRename = false; /**< 是否存在待应用的重命名请求。 */
|
||||||
|
|
||||||
int m_pendingMeanFilterLane = -1; /**< 待构建的均值滤波信号所属网格下标(-1 表示无请求)。 */
|
int m_pendingMeanFilterLane = -1; /**< 待构建的均值滤波信号所属网格下标(-1 表示无请求)。 */
|
||||||
int m_pendingWeakBoostLane = -1; /**< 待构建的弱波增强信号所属网格下标(-1 表示无请求)。 */
|
int m_pendingWeakPeakLane = -1; /**< 待构建的弱峰调整信号所属网格下标(-1 表示无请求)。 */
|
||||||
std::vector<MeanFilterBuild> m_pendingMeanFilterItems; /**< 待构建的信号及其参数。 */
|
std::vector<MeanFilterBuild> m_pendingMeanFilterItems; /**< 待构建的信号及其参数。 */
|
||||||
std::vector<WeakBoostBuild> m_pendingWeakBoostItems; /**< 待构建的弱波增强信号及其参数。 */
|
std::vector<WeakPeakBuild> m_pendingWeakPeakItems; /**< 待构建的弱峰调整信号及其参数。 */
|
||||||
int m_boostDragLane = -1; /**< 正在拖动滑块的面板所属网格。 */
|
|
||||||
bool m_mouseOverPanel = false; /**< 上一帧鼠标是否落在某个实验性功能面板上。 */
|
bool m_mouseOverPanel = false; /**< 上一帧鼠标是否落在某个实验性功能面板上。 */
|
||||||
|
int m_peakDragLane = -1; /**< 正在拖动弱峰滑块的面板所属网格。 */
|
||||||
|
SignalRef m_peakDragRef; /**< 正在拖动弱峰滑块的**信号**(每个信号一套参数)。 */
|
||||||
|
/**
|
||||||
|
* @brief 正在编辑「迭代次数 N 上限」时,输入框内灰色提示用的**当前上限值**。
|
||||||
|
*
|
||||||
|
* 输入框以空内容开始,空内容时显示这个值作为提示;进入编辑时记录,提交后清零。
|
||||||
|
*/
|
||||||
|
int m_peakEditCurrentMax = 0;
|
||||||
/**
|
/**
|
||||||
* @brief 本帧是否使用「快速绘制」(交互期间关闭波形与派生曲线的抗锯齿)。
|
* @brief 本帧是否使用「快速绘制」(交互期间关闭波形与派生曲线的抗锯齿)。
|
||||||
*
|
*
|
||||||
@@ -1753,18 +1769,18 @@ namespace jdi
|
|||||||
};
|
};
|
||||||
mutable std::deque<MeanCurveEntry> m_meanCurveCache;
|
mutable std::deque<MeanCurveEntry> m_meanCurveCache;
|
||||||
|
|
||||||
/** 弱波增强曲线缓存项(按「信号指针 + 增强等级百分比 + 迭代次数」标识)。 */
|
/** 弱峰调整曲线缓存项(按「信号指针 + 调整幅度百分比 + 迭代次数」标识)。 */
|
||||||
struct WeakBoostCurveEntry
|
struct WeakPeakCurveEntry
|
||||||
{
|
{
|
||||||
const SignalData* signal = nullptr;
|
const SignalData* signal = nullptr;
|
||||||
int levelPercent = 50; /**< 弱波增强等级(0–100)。 */
|
int amountPercent = 0; /**< 弱峰调整幅度(-400–400,默认 0 = 不改动)。 */
|
||||||
int iterations = 1; /**< 迭代次数 N(1–10)。 */
|
int iterations = 1; /**< 迭代次数 N(1–10)。 */
|
||||||
std::vector<float> values; /**< 与 signal->values 等长;缺失处为 NaN。 */
|
std::vector<float> values; /**< 与 signal->values 等长;缺失处为 NaN。 */
|
||||||
};
|
};
|
||||||
mutable std::deque<WeakBoostCurveEntry> m_weakBoostCurveCache;
|
mutable std::deque<WeakPeakCurveEntry> m_weakPeakCurveCache;
|
||||||
|
|
||||||
// ---- 异步派生曲线计算(后台线程 + 双缓冲)----
|
// ---- 异步派生曲线计算(后台线程 + 双缓冲)----
|
||||||
// 设计:预览曲线(均值滤波 / 弱波增强)在窗口很大时**不在绘制线程里计算**,而是交给后台
|
// 设计:预览曲线(均值滤波 / 弱峰调整)在窗口很大时**不在绘制线程里计算**,而是交给后台
|
||||||
// 线程;绘制线程本帧先用上一条已算好的曲线顶替(双缓冲),后台算完后通过
|
// 线程;绘制线程本帧先用上一条已算好的曲线顶替(双缓冲),后台算完后通过
|
||||||
// `kMessagePlotDerivedReady` 通知宿主重绘,从而切换为最新曲线。这样滑块 / 光标 / 视界 /
|
// `kMessagePlotDerivedReady` 通知宿主重绘,从而切换为最新曲线。这样滑块 / 光标 / 视界 /
|
||||||
// 缩放等交互的反馈始终跟手,不会被计算拖住,同时把重计算放到后台充分利用多核。
|
// 缩放等交互的反馈始终跟手,不会被计算拖住,同时把重计算放到后台充分利用多核。
|
||||||
@@ -1911,6 +1927,22 @@ namespace jdi
|
|||||||
*/
|
*/
|
||||||
bool SceneCacheMatches() const;
|
bool SceneCacheMatches() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief **单个网格**的内容指纹(用于该网格自己的「信号图层」)。
|
||||||
|
*
|
||||||
|
* 图层位图里只含该网格的内容(底色 + 网格线 + 波形),位图高度 = 该网格高度、
|
||||||
|
* 内部以 `y=0` 为网格顶边,因此该指纹只需要「网格高度 + 幅值范围 + 本网格各信号的
|
||||||
|
* 可见性与外观 + 图例文案」。
|
||||||
|
*
|
||||||
|
* ⚠️ 这**必须**是逐网格的:早期版本用全局内容指纹,于是一个网格的幅值变化
|
||||||
|
* 会同时作废**全部**网格的图层(拖拽时纵向分量让 5 个网格排队重画 → 图层内容
|
||||||
|
* 相互不一致 = 错误绘制;且每帧都入队 → 永远补不完)。
|
||||||
|
*
|
||||||
|
* @param [IN] laneIndex 网格下标。
|
||||||
|
* @return 指纹值。
|
||||||
|
*/
|
||||||
|
std::uint64_t LaneLayerHash(int laneIndex) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 计算场景内容指纹(信号集合、显示样式、网格幅值、图例文案)。
|
* @brief 计算场景内容指纹(信号集合、显示样式、网格幅值、图例文案)。
|
||||||
*
|
*
|
||||||
@@ -1919,16 +1951,22 @@ namespace jdi
|
|||||||
*
|
*
|
||||||
* @param [IN] includeStyles 是否把逐信号外观样式(颜色/不透明度/线宽/模式)计入指纹。
|
* @param [IN] includeStyles 是否把逐信号外观样式(颜色/不透明度/线宽/模式)计入指纹。
|
||||||
* @param [IN] includeDerived 是否把派生预览曲线的开关与参数计入指纹。
|
* @param [IN] includeDerived 是否把派生预览曲线的开关与参数计入指纹。
|
||||||
|
* @param [IN] includeLaneRange 是否把各网格的**幅值范围**(`yMin/yMax`)计入指纹。
|
||||||
|
* ⚠️ **拖拽平移时的纵向分量**会每帧小幅修改被拖网格的幅值范围;若它参与
|
||||||
|
* “宽基底可用性”判据,每一帧都会退化成整幅重绘(质量下降 + 卡顿)。
|
||||||
|
* 因此宽基底判据用 `includeLaneRange = false` 的几何指纹,
|
||||||
|
* 幅值变化改由**逐网格图层自己的指纹**处理(只有该网格需要重画)。
|
||||||
* @return 指纹值。
|
* @return 指纹值。
|
||||||
*/
|
*/
|
||||||
std::uint64_t SceneContentHash(bool includeStyles = true, bool includeDerived = true) const;
|
std::uint64_t SceneContentHash(bool includeStyles = true, bool includeDerived = true,
|
||||||
|
bool includeLaneRange = true) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 场景内容中**不含逐信号外观样式**的指纹。
|
* @brief 场景内容中**不含逐信号外观样式**的指纹。
|
||||||
*
|
*
|
||||||
* 只包含网格结构、视图几何、网格幅值、信号集合与可见性、图例文案、选中项等
|
* 只包含网格结构、视图几何、信号集合与可见性、图例文案、选中项等
|
||||||
* "变化后必须整幅重建"的内容;逐信号的 `color / opacity / lineWidth / mode`
|
* "变化后必须整幅重建"的内容;逐信号的 `color / opacity / lineWidth / mode`
|
||||||
* 一律不参与。
|
* 一律不参与,**各网格的幅值范围(`yMin/yMax`)也不参与**(由逐网格图层指纹负责)。
|
||||||
*
|
*
|
||||||
* 用途:外观样式变化时(典型场景是拖动自定义颜色的滑块)波形几何完全不变,
|
* 用途:外观样式变化时(典型场景是拖动自定义颜色的滑块)波形几何完全不变,
|
||||||
* 只要该指纹一致,就可以只重画"颜色变了的那个网格"再写回缓存,
|
* 只要该指纹一致,就可以只重画"颜色变了的那个网格"再写回缓存,
|
||||||
@@ -1941,7 +1979,7 @@ namespace jdi
|
|||||||
/**
|
/**
|
||||||
* @brief 装饰层(图例 + Y 轴刻度)的指纹:含外观样式,但**不含派生曲线参数**。
|
* @brief 装饰层(图例 + Y 轴刻度)的指纹:含外观样式,但**不含派生曲线参数**。
|
||||||
*
|
*
|
||||||
* 拖弱波增强等级/迭代次数滑块时,图例与 Y 轴刻度不变,仅网格内预览曲线变化;
|
* 拖弱峰调整等级/迭代次数滑块时,图例与 Y 轴刻度不变,仅网格内预览曲线变化;
|
||||||
* 因此装饰层缓存不应因参数变化而失效(否则拖滑块每帧都要重绘图例与刻度)。
|
* 因此装饰层缓存不应因参数变化而失效(否则拖滑块每帧都要重绘图例与刻度)。
|
||||||
*
|
*
|
||||||
* @return 指纹值。
|
* @return 指纹值。
|
||||||
@@ -1971,20 +2009,18 @@ namespace jdi
|
|||||||
*
|
*
|
||||||
* 它**只影响网格内容的绘制**(不影响网格结构/视图几何),因此不进入
|
* 它**只影响网格内容的绘制**(不影响网格结构/视图几何),因此不进入
|
||||||
* `SceneGeometryHash`,而是与外观样式一起作为“局部重画”的判据:
|
* `SceneGeometryHash`,而是与外观样式一起作为“局部重画”的判据:
|
||||||
* 拖动弱波增强的等级/迭代次数滑块时,只需重画受影响的网格,
|
* 拖动弱峰调整的等级/迭代次数滑块时,只需重画受影响的网格,
|
||||||
* 不必整幅重绘(也不会把静态场景缓存整体作废)。
|
* 不必整幅重绘(也不会把静态场景缓存整体作废)。
|
||||||
*/
|
*/
|
||||||
struct SceneDerivedEntry
|
struct SceneDerivedEntry
|
||||||
{
|
{
|
||||||
int edgeCenter = 0; /**< 中心轴边沿计数开关。 */
|
int edgeCenter = 0; /**< 中心轴边沿计数开关。 */
|
||||||
int meanFilter = 0; /**< 均值滤波开关。 */
|
int meanFilter = 0; /**< 均值滤波开关。 */
|
||||||
int weakBoost = 0; /**< 弱波增强开关。 */
|
int weakPeak = 0; /**< 弱峰调整开关。 */
|
||||||
int weakBoostLevelPercent = 0; /**< 弱波增强等级(0–100)。 */
|
/** 各条目展开为 [文件, 信号, 参数…],按原顺序(稳定)。 */
|
||||||
int weakBoostIterations = 1; /**< 弱波增强迭代次数 N。 */
|
|
||||||
/** 各条目展开为 [文件, 信号, 均值点数 X, 迭代次数 N],按原顺序(稳定)。 */
|
|
||||||
std::vector<int> edgeCenterItems;
|
std::vector<int> edgeCenterItems;
|
||||||
std::vector<int> meanFilterItems;
|
std::vector<int> meanFilterItems;
|
||||||
std::vector<int> weakBoostItems;
|
std::vector<int> weakPeakItems;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -2222,20 +2258,25 @@ namespace jdi
|
|||||||
mutable int m_decorOriginX = 0;
|
mutable int m_decorOriginX = 0;
|
||||||
mutable int m_decorOriginY = 0;
|
mutable int m_decorOriginY = 0;
|
||||||
|
|
||||||
// ---- 每网格「信号图层」:双缓冲 + 后台绘制(前台只做贴图 / 坐标偏移) ----
|
// ---- 每网格「信号图层」(前台同步维护;交互帧只做贴图 / 坐标偏移) ----
|
||||||
//
|
//
|
||||||
// 平移只改变绘制坐标、不改变画面内容。因此**每个网格各自维护一块离屏位图**
|
// 平移只改变绘制坐标、不改变画面内容。因此**每个网格各自维护一块离屏位图**
|
||||||
// (宽度 = 视界宽 ×3:左右各多渲染半个视界),称为该网格的「信号图层」。
|
// (宽度 = 视界宽 + 左右各半个视界的余量),称为该网格的「信号图层」。
|
||||||
// 图层只含基底(底色 + 网格线 + 波形 + 派生曲线),**不含装饰**(Y 轴刻度 / 图例)——
|
// 图层只含基底(底色 + 网格线 + 波形),**不含装饰**(Y 轴刻度 / 图例)——
|
||||||
// 装饰锚定在网格左缘、不随时间视界移动,若烘进图层会被平移带走(重影),
|
// 装饰锚定在网格左缘、不随时间视界移动,若烘进图层会被平移带走(重影),
|
||||||
// 因此装饰由前台单独绘制(见 `m_decorCapturedRects` 那套缓存)。
|
// 因此装饰由前台单独绘制(见 `m_decorCapturedRects` 那套缓存)。
|
||||||
//
|
//
|
||||||
// **双缓冲**:每个网格有两块图层(`m_layerFront` 前台读取 / `m_layerBack` 后台填充),
|
// ⚠️ **图层一律在 UI 线程内同步维护**(`Paint` 全程持有 `m_renderMutex`),
|
||||||
// 后台绘制完成后再整体交换。前台**永远只读 `m_layerFront`**,因此后台正在绘制时
|
// 因为渲染图层会临时改写 `m_area` / `m_plotRect` / `Tab().ViewMin()/ViewMax()` /
|
||||||
// 前台仍能拿到上一份可用像素,绘制过程不影响交互响应。
|
// 各网格矩形 —— 这些状态**同时被 UI 线程的消息处理无锁读取**
|
||||||
|
// (`OnMouseMove` 平移读视界、命中测试读网格矩形)。若把图层渲染搬到后台线程,
|
||||||
|
// 后台的"保存 → 改写 → 恢复"会**覆盖用户正在进行的平移**(视界被弹回原位),
|
||||||
|
// 命中测试也会读到错位的网格 → 表现为「拖动时视界跳回 / 波形抖动 / 绘制错乱」。
|
||||||
|
// 这正是先前"后台图层线程"被移除的原因:**不要把图层渲染移回后台**。
|
||||||
//
|
//
|
||||||
// **后台绘制**:跨度 / 几何 / 外观变化时,后台线程池把各网格的新图层画进后台缓冲;
|
// 每个图层记录自己的时间映射(`LaneLayer::wideMin`):不同时刻渲染出的图层锚点
|
||||||
// 若前台已经要刷新而后台还没画好,就继续用旧的前台缓冲「应付」交互,不解切换。
|
// 并不相同,滚动与贴图一律**逐网格**按各自的 `wideMin` 计算,**绝不共用一个全局锚点**
|
||||||
|
// (共用全局锚点会让各网格被平移不同的真实距离 → 网格之间时间错位)。
|
||||||
/**
|
/**
|
||||||
* @brief 单个网格的「信号图层」(一块只含该网格基底的离屏位图)。
|
* @brief 单个网格的「信号图层」(一块只含该网格基底的离屏位图)。
|
||||||
*/
|
*/
|
||||||
@@ -2245,9 +2286,18 @@ namespace jdi
|
|||||||
HBITMAP bitmap = nullptr; /**< 离屏位图。 */
|
HBITMAP bitmap = nullptr; /**< 离屏位图。 */
|
||||||
HGDIOBJ oldBitmap = nullptr; /**< DC 原选位图。 */
|
HGDIOBJ oldBitmap = nullptr; /**< DC 原选位图。 */
|
||||||
int width = 0; /**< 位图宽度(含左右各半屏余量)。 */
|
int width = 0; /**< 位图宽度(含左右各半屏余量)。 */
|
||||||
int height = 0; /**< 位图高度(= 网格高)。 */
|
int height = 0; /**< 位图高度(含上下各半个网格高的余量)。 */
|
||||||
|
int marginY = 0; /**< 位图在**可见网格上方**预留的像素行数(纵向预渲染余量)。 */
|
||||||
double wideMin = 0.0; /**< 位图 x=0 对应的时间。 */
|
double wideMin = 0.0; /**< 位图 x=0 对应的时间。 */
|
||||||
double span = 0.0; /**< 渲染时的视界跨度(决定像素/时间)。 */
|
double span = 0.0; /**< 渲染时的视界跨度(决定像素/时间)。 */
|
||||||
|
/**
|
||||||
|
* @brief 渲染时的**纵向锚点**:该网格的 `yMax`(可见窗口顶行对应的数值)。
|
||||||
|
*
|
||||||
|
* 与 `wideMin` 同理,平移(纵向是幅值平移)时只移动**贴图偏移**、不重画像素:
|
||||||
|
* 需要贴到屏幕顶行的位图行 = `marginY + round((anchorYMax - 当前 yMax) * pxPerValue)`。
|
||||||
|
*/
|
||||||
|
double anchorYMax = 0.0;
|
||||||
|
double pxPerValue = 0.0; /**< 位图纵向像素 / 数值单位(= 可见网格高 / 幅值跨度)。 */
|
||||||
std::uint64_t key = 0; /**< 内容指纹(含跨度、网格几何与外观)。 */
|
std::uint64_t key = 0; /**< 内容指纹(含跨度、网格几何与外观)。 */
|
||||||
bool valid = false; /**< 是否有可显示的像素。 */
|
bool valid = false; /**< 是否有可显示的像素。 */
|
||||||
};
|
};
|
||||||
@@ -2263,88 +2313,117 @@ namespace jdi
|
|||||||
* @param [IN] laneIndex 网格下标。
|
* @param [IN] laneIndex 网格下标。
|
||||||
* @param [IN,OUT] layer 目标图层(会被写入尺寸 / 时间映射 / 指纹)。
|
* @param [IN,OUT] layer 目标图层(会被写入尺寸 / 时间映射 / 指纹)。
|
||||||
* @param [IN] stripX 只渲染位图的这一段 x 区间(滚动时补画细条用)。
|
* @param [IN] stripX 只渲染位图的这一段 x 区间(滚动时补画细条用)。
|
||||||
* @param [IN] stripW 该区间的宽度;传 0 表示渲染整块。
|
* @param [IN] stripW 该区间的宽度;传 0 表示横向整块。
|
||||||
|
* @param [IN] stripY 只渲染位图的这一段 y 区间(纵向滚动补条用)。
|
||||||
|
* @param [IN] stripH 该区间的高度;传 0 表示纵向整块。
|
||||||
|
* @param [IN] wideMin 位图 `x=0` 对应的时间。
|
||||||
|
* **必须与调用方的意图一致**:整块重画传 `LayerDefaultWideMin()`(以当前视界居中);
|
||||||
|
* 滚动补细条则传 `layer.wideMin`(与该图层已有像素同一映射,否则会留下接缝)。
|
||||||
* @return 渲染成功返回 true。
|
* @return 渲染成功返回 true。
|
||||||
*/
|
*/
|
||||||
bool RenderLaneLayerRegion(int laneIndex, LaneLayer& layer, int stripX, int stripW);
|
bool RenderLaneLayerRegion(int laneIndex, LaneLayer& layer, int stripX, int stripW, int stripY,
|
||||||
|
int stripH, double wideMin);
|
||||||
|
/**
|
||||||
|
* @brief 前台补画各网格的**分界线**(网格顶部那条较重的横线)。
|
||||||
|
*
|
||||||
|
* ⚠️ 分界线锚定在网格的**屏幕位置**(`lane.rect.Y`),幅值平移时它**不跟着移动**,
|
||||||
|
* 因此不能烘进"纵向可平移"的图层里(否则纵向拖动时它会跟着波形跑)。
|
||||||
|
* 整幅重绘由 `PaintLane` 直接绘制;宽基底快路径(贴图层像素)必须在这里补画一次。
|
||||||
|
*/
|
||||||
|
void PaintLaneDividers(gd::Graphics& g) const;
|
||||||
|
/** @brief 「以当前视界居中」的默认图层映射(位图 `x=0` 对应的时间)。 */
|
||||||
|
double LayerDefaultWideMin() const;
|
||||||
|
/** @brief 图层位图的横向余量(左右各半屏 → 可横向平移 ±半屏)。 */
|
||||||
|
int LayerMarginX() const;
|
||||||
|
/**
|
||||||
|
* @brief 图层位图的纵向余量(上下各半个网格高 → 可纵向平移 ±半个网格高)。
|
||||||
|
*
|
||||||
|
* ⚠️ 纵向余量让「拖拽平移的纵向分量」(幅值平移)与横向平移一样**只改贴图偏移**,
|
||||||
|
* 不必每帧重画整块图层 —— 这正是「左右平移流畅、上下平移卡顿」的根因所在。
|
||||||
|
* 余量越大可连续拖拽的范围越大,但每个横向补条 / 整块重画的代价也按余量线性增长
|
||||||
|
*(因此取「半个网格高」:一整屏的额外内容已足够,且不拖慢已经流畅的横向平移)。
|
||||||
|
*/
|
||||||
|
int LayerMarginY(float laneHeight) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 以当前视界为中心重建全部网格图层(同步;跨度变化 / 首帧时使用)。
|
* @brief 以当前视界为中心重建全部网格图层(同步;跨度变化 / 首帧 / 图层缺失时使用)。
|
||||||
* @return 全部成功返回 true。
|
* @return 全部成功返回 true。
|
||||||
*/
|
*/
|
||||||
bool RebuildWideBuffer();
|
bool RebuildWideBuffer();
|
||||||
/**
|
/**
|
||||||
* @brief 让全部图层"滚动"以跟随视界:整数像素自贴图平移 + 只补画新暴露的细条。
|
* @brief 让各图层"滚动"以跟随视界:逐网格整数像素自贴图平移 + 只补画新暴露的细条。
|
||||||
* @return 图层仍可用返回 true。
|
*
|
||||||
|
* ⚠️ **本函数绝不做"整块重画"**:内容指纹过期 / 锚点离视界太远的网格只被排进
|
||||||
|
* `m_layerRebuildPending`(随后由 `RenderPendingLaneRebuilds` 按预算补画),本帧仍按
|
||||||
|
* 现有像素做整数像素平移,因此**前台线程永远不会因为"图层没准备好"而卡顿**。
|
||||||
*/
|
*/
|
||||||
bool RollWideBuffer();
|
void RollWideBuffer();
|
||||||
/**
|
/**
|
||||||
* @brief 逐个网格截取图层子矩形贴图到后备缓冲区(O(1) 位图搬运)。
|
* @brief 逐个网格截取图层子矩形贴图到后备缓冲区(O(1) 位图搬运)。
|
||||||
|
*
|
||||||
|
* 图层只覆盖"视界 ± 半屏":若当前视界有一部分落在图层之外,这里**只贴有像素的那一段**
|
||||||
|
* (其余留由底色填充,下一帧补齐),而不是跳过整块 —— 这正是"未绘制的部分先暂时不管"。
|
||||||
* @param [IN,OUT] g 绘图上下文。
|
* @param [IN,OUT] g 绘图上下文。
|
||||||
* @return 至少贴了若干网格返回 true。
|
* @return 至少贴了若干像素返回 true。
|
||||||
*/
|
*/
|
||||||
bool BlitWideBuffer(gd::Graphics& g) const;
|
bool BlitWideBuffer(gd::Graphics& g) const;
|
||||||
/**
|
/**
|
||||||
* @brief 全部图层是否已就绪且跨度与当前视界一致(**不要求内容也一致**)。
|
* @brief 全部图层是否**结构可用**(数量 / 位图尺寸 / 视界跨度都与当前状态一致)。
|
||||||
*
|
*
|
||||||
* 内容一致与否由后台线程负责跟踪:只要跨度一致、图层能覆盖当前视界,
|
* 结构不可用意味着"像素的时间映射已经不对",只能整批重建(跨度变化 / 布局变化 / 首帧);
|
||||||
* 前台就直接用现成像素“应付”交互(内容稍旧的一两帧由后台画好后交换),
|
* 内容指纹是否最新由 `LaneLayerReady()` 逐个判断。
|
||||||
* 这样样式/参数变化也不会退化成整幅重绘。
|
* @return 可以直接滚动 + 贴图层像素返回 true。
|
||||||
* @return 可以直接贴图层像素返回 true。
|
|
||||||
*/
|
*/
|
||||||
bool LayersReadyForView() const;
|
bool LayersStructurallyReady() const;
|
||||||
|
/**
|
||||||
|
* @brief 某个网格的图层是否**完全就绪**(结构可用且内容指纹与当前状态一致)。
|
||||||
|
* @param [IN] laneIndex 网格下标。
|
||||||
|
* @return 就绪返回 true。
|
||||||
|
*/
|
||||||
|
bool LaneLayerReady(int laneIndex) const;
|
||||||
|
/** @brief 全部网格图层是否都完全就绪(用于判断能否做"结算"整幅精确帧)。 */
|
||||||
|
bool LanesAllReadyForView() const;
|
||||||
|
/** @brief 把某网格排进"待重画"队列(去重;视界/跨度后续变化时会在渲染时再次校验)。 */
|
||||||
|
void QueueLaneRebuild(int laneIndex);
|
||||||
|
/**
|
||||||
|
* @brief 按预算补画"待重画"的网格图层(**本帧最多 `budget` 个**)。
|
||||||
|
*
|
||||||
|
* 这是"前台不做整批重绘"的关键:即使 5 个网格都需要重画,也一帧只画一个,
|
||||||
|
* 其余留到后续帧(缺的部分留空 / 用旧像素)。调用方若发现队列非空应请求再次重绘。
|
||||||
|
* @param [IN] budget 本次最多重画的网格数量。
|
||||||
|
* @return 队列中仍有待重画网格返回 true。
|
||||||
|
*/
|
||||||
|
bool RenderPendingLaneRebuilds(int budget);
|
||||||
/** @brief 使全部图层失效。 */
|
/** @brief 使全部图层失效。 */
|
||||||
void InvalidateWideBuffer();
|
void InvalidateWideBuffer();
|
||||||
/** @brief 释放全部图层位图(前后台都释放)。 */
|
/** @brief 释放全部图层位图。 */
|
||||||
void ReleaseWideBuffer();
|
void ReleaseWideBuffer();
|
||||||
|
|
||||||
|
std::vector<LaneLayer> m_layerFront; /**< 各网格的「信号图层」(与 `m_lanes` 同序)。 */
|
||||||
|
/** 待重画的网格下标(去重;`RollWideBuffer` 登记,`RenderPendingLaneRebuilds` 消费)。 */
|
||||||
|
std::vector<int> m_layerRebuildPending;
|
||||||
/**
|
/**
|
||||||
* @brief 为"内容与前台图层不一致"的网格排一次后台渲染任务(去重)。
|
* @brief 图层元数据的互斥(当前图层只在 UI 线程维护,故它实际不承担跨线程保护;
|
||||||
*
|
* 保留是为了给 `BlitWideBuffer()` 这类 `const` 读取路径一个明确的一致性边界)。
|
||||||
* 后台渲染完成后会与前台缓冲交换并通知宿主重绘,前台因此始终有立即可用的像素。
|
|
||||||
*/
|
*/
|
||||||
void ScheduleStaleLayerRenders();
|
mutable std::mutex m_layerMutex;
|
||||||
|
std::recursive_mutex m_renderMutex; /**< 串行化"改写渲染状态"的绘制(整帧 / 单网格图层);可重入。 */
|
||||||
/** @brief 确保后台图层绘制线程已启动。 */
|
/** 已渲染的图层区域数量(原子,仅用于验证 / 诊断)。 */
|
||||||
void EnsureLayerWorker();
|
|
||||||
/** @brief 停止后台图层绘制线程。 */
|
|
||||||
void StopLayerWorker();
|
|
||||||
/** @brief 后台图层绘制线程主循环。 */
|
|
||||||
void LayerWorkerLoop();
|
|
||||||
|
|
||||||
/** 单个网格图层任务:只需网格下标(参数在渲染时按最新状态取)。 */
|
|
||||||
struct LayerJob
|
|
||||||
{
|
|
||||||
int laneIndex = -1;
|
|
||||||
std::uint64_t key = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
std::vector<LaneLayer> m_layerFront; /**< 前台读取(后台不会就地改写)。 */
|
|
||||||
std::vector<LaneLayer> m_layerBack; /**< 后台填充。 */
|
|
||||||
mutable std::mutex m_layerMutex; /**< 保护前后台切换 / 元数据读取 / 锚点(const 方法也要读,故 mutable)。 */
|
|
||||||
std::recursive_mutex m_renderMutex; /**< 串行化“改写渲染状态”的绘制(前台整帧 vs 后台单网格);可重入(整帧内部会再调图层渲染)。 */
|
|
||||||
double m_layerAnchorMin = 0.0; /**< 图层滚动锚点:视界左端对应的时间。 */
|
|
||||||
bool m_layerAnchorValid = false; /**< 锚点是否有效。 */
|
|
||||||
|
|
||||||
std::thread m_layerWorker; /**< 后台图层绘制线程。 */
|
|
||||||
std::mutex m_layerJobMutex; /**< 任务队列互斥。 */
|
|
||||||
std::condition_variable m_layerJobCv; /**< 任务到来 / 退出通知。 */
|
|
||||||
std::vector<LayerJob> m_layerJobs; /**< 待渲染的网格任务。 */
|
|
||||||
bool m_layerWorkerStop = false; /**< 请求退出后台线程。 */
|
|
||||||
/**
|
|
||||||
* @brief 连续排了多少次"图层重建"却始终没与前台一致。
|
|
||||||
*
|
|
||||||
* 正常情况下一两次就会收敛(后台画完→交换→指纹一致)。若持续增长,说明后台渲染
|
|
||||||
* 算出的指纹与前台不一致(例如某个参与指纹的状态没被正确还原),此时改为**同步重建**
|
|
||||||
* 一次强行收敛,避免"前台永远不等后台、后台永远画不好"的死循环。
|
|
||||||
*/
|
|
||||||
int m_layerRescheduleStreak = 0;
|
|
||||||
/** 前台最近一次 `Paint` 的时刻(毫秒,`GetTickCount64`)。后台据此"让路",不与前台抢渲染锁。 */
|
|
||||||
mutable std::atomic<unsigned long long> m_lastPaintTick{0};
|
|
||||||
/** 后台线程已完成绘制的图层数量(原子,仅用于验证/诊断)。 */
|
|
||||||
mutable std::atomic<std::uint64_t> m_layerRenderCount{0};
|
mutable std::atomic<std::uint64_t> m_layerRenderCount{0};
|
||||||
|
|
||||||
bool m_renderWide = false; /**< 正在渲染信号图层(重入 `PaintLane` 时跳过装饰)。 */
|
bool m_renderWide = false; /**< 正在渲染信号图层(重入 `PaintLane` 时跳过装饰)。 */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 渲染信号图层时,横向网格线(幅值刻度)的「参考跨度 / 参考高度」。
|
||||||
|
*
|
||||||
|
* ⚠️ **必须是前台可见网格的值**,而不是图层画布的(含纵向余量的)值:
|
||||||
|
* 图层的幅值刻度必须以该跨度为基准取「整齐步长」,否则画布里的网格线密度与
|
||||||
|
* 整幅渲染不一致 —— 贴图后拖动时网格线会跳变。步长一致时刻度落在同一个
|
||||||
|
* 绝对值格点上,于是纵向平移就是纯粹的像素平移(逐像素同源)。
|
||||||
|
*/
|
||||||
|
double m_wideTickSpan = 0.0;
|
||||||
|
float m_wideTickHeight = 0.0f;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 本次 `PaintLane` 只绘制「装饰」(Y 轴刻度胶囊 + 图例 + 网格分界线)。
|
* @brief 本次 `PaintLane` 只绘制「装饰」(Y 轴刻度胶囊 + 图例 + 网格分界线)。
|
||||||
*
|
*
|
||||||
@@ -2385,6 +2464,42 @@ namespace jdi
|
|||||||
std::uint64_t m_panelGlobalKey = 0; /**< 上一层对应的全局键。 */
|
std::uint64_t m_panelGlobalKey = 0; /**< 上一层对应的全局键。 */
|
||||||
std::vector<PanelSlot> m_panelSlots; /**< 上一层缓存的面板(本帧比对用)。 */
|
std::vector<PanelSlot> m_panelSlots; /**< 上一层缓存的面板(本帧比对用)。 */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 浮动面板的**层序**(从下到上)。
|
||||||
|
*
|
||||||
|
* 面板允许互相重叠,因此必须有明确的层序:
|
||||||
|
* * **绘制**按此顺序从下往上;**命中测试**从后往前(最上层优先)。
|
||||||
|
* * 点击某个面板会把它移到队尾(置顶),因此重叠时总能操作到"用户想要"的那一个
|
||||||
|
* (否则上层面板的 ✕ / 标题栏会被下层控件的命中判定抢走)。
|
||||||
|
* * 新出现的面板追加到队尾(在最上层),不会被已有面板盖住。
|
||||||
|
*/
|
||||||
|
struct PanelRef
|
||||||
|
{
|
||||||
|
int laneIndex = -1; /**< 所属网格。 */
|
||||||
|
int kind = 0; /**< `EdgePanelKind` 的整型值。 */
|
||||||
|
|
||||||
|
bool operator==(const PanelRef& other) const
|
||||||
|
{
|
||||||
|
return laneIndex == other.laneIndex && kind == other.kind;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/** 同步 `m_panelOrder` 与当前"可见的面板集合"(新增的追加到最上层;消失的移除)。 */
|
||||||
|
void SyncPanelOrder() const;
|
||||||
|
/** @brief 把某个面板移到最上层(点击面板时调用)。 */
|
||||||
|
void BringPanelToFront(int laneIndex, int kind);
|
||||||
|
/**
|
||||||
|
* @brief 从最上层往下找出包含该点的面板。
|
||||||
|
* @param [IN] x 客户区横坐标。
|
||||||
|
* @param [IN] y 客户区纵坐标。
|
||||||
|
* @param [OUT] laneIndex 命中面板所属网格。
|
||||||
|
* @param [OUT] kind 命中面板的种类(`EdgePanelKind`)。
|
||||||
|
* @return 命中返回 true。
|
||||||
|
*/
|
||||||
|
bool TopPanelAtPoint(float x, float y, int& laneIndex, int& kind) const;
|
||||||
|
|
||||||
|
mutable std::vector<PanelRef> m_panelOrder; /**< 浮动面板层序(队尾 = 最上层;mutable 供 const 光标查询同步层序)。 */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 查询坐标命中的图例条目。
|
* @brief 查询坐标命中的图例条目。
|
||||||
* @param [IN] x 客户区横坐标。
|
* @param [IN] x 客户区横坐标。
|
||||||
|
|||||||
+1396
-749
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
+66
-48
@@ -374,13 +374,21 @@ namespace jdi
|
|||||||
using EdgeCenterItem = MeanFilterItem;
|
using EdgeCenterItem = MeanFilterItem;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 「弱波增强」中某个信号自己的条目。
|
* @brief 「弱峰调整」中某个信号自己的参数条目。
|
||||||
*
|
*
|
||||||
* 目前只用到 `ref`(增强等级属于整个网格,统一保存在 `weakBoostLevel` 中);
|
* 与「均值滤波」完全一致:**每个被勾选的信号各自持有参数**
|
||||||
* 复用 `MeanFilterItem` 结构是为了与均值滤波/中心轴共享
|
*(调整幅度 -400% – +400% + 迭代次数 N),因此同一网格内不同信号可以使用不同强度。
|
||||||
* 「条目查找 + 信号插入/删除时的引用重映射」等既有实现。
|
*
|
||||||
|
* ⚠️ `iterations` 的**上限由用户自己设定**(`maxIterations`):面板上「迭代次数 N」
|
||||||
|
* 滑块的量程就是 `1 … maxIterations`,点击滑块右侧的数值可以把该上限改成其它正整数。
|
||||||
*/
|
*/
|
||||||
using WeakBoostItem = MeanFilterItem;
|
struct WeakPeakItem
|
||||||
|
{
|
||||||
|
SignalRef ref; /**< 目标信号。 */
|
||||||
|
int amountPercent = 0; /**< 调整幅度(-400–400,默认 0 = 不改动)。 */
|
||||||
|
int iterations = 1; /**< 迭代次数 N(1 … `maxIterations`)。 */
|
||||||
|
int maxIterations = 10; /**< 「迭代次数 N」滑块的上限(用户可在面板上修改,恒 ≥ 1)。 */
|
||||||
|
};
|
||||||
|
|
||||||
std::wstring name; /**< 网格名称。 */
|
std::wstring name; /**< 网格名称。 */
|
||||||
std::vector<SignalRef> signals; /**< 该网格包含的信号(按加入顺序)。 */
|
std::vector<SignalRef> signals; /**< 该网格包含的信号(按加入顺序)。 */
|
||||||
@@ -501,53 +509,46 @@ namespace jdi
|
|||||||
double meanFilterPanelY = -1.0;
|
double meanFilterPanelY = -1.0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 「实验性功能 → 弱波增强」开关。
|
* @brief 「实验性功能 → 弱峰调整」开关。
|
||||||
*
|
*
|
||||||
* 开启后为本网格内**被勾选**的信号额外绘制一条「弱波增强」曲线:
|
* 开启后为本网格内**被勾选**的信号额外绘制一条「弱峰调整」曲线:
|
||||||
* 识别纹波的波峰/波谷,把“比左右同类极值更弱”的波峰上拉、波谷下拉,
|
* 识别纹波的波峰/波谷,把“比左右同类极值更弱”的波峰上拉、波谷下拉,
|
||||||
* 并按「弱波增强等级」在左右两个同类极值之间取目标值,使其恢复与周边一致的振幅。
|
* 并按各信号自己的「增强等级」在左右两个同类极值之间取目标值,使其恢复与周边振幅。
|
||||||
* 这条曲线只是叠加显示,不会作为信号加入左侧列表。
|
* 这条曲线只是叠加显示,不会作为信号加入左侧列表。
|
||||||
*/
|
*/
|
||||||
bool weakBoost = false;
|
bool weakPeak = false;
|
||||||
|
|
||||||
/** 弱波增强等级(0–1),0% = 目标取左右极值中较小/较大的那一个,100% = 取另一个。 */
|
|
||||||
double weakBoostLevel = 0.5;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 弱波增强的迭代次数 N(1–10)。
|
* @brief 参与弱峰调整预览的信号及其**各自的参数**(只包含被勾选的信号)。
|
||||||
*
|
*
|
||||||
* 与「均值滤波」的迭代次数 N 一样表示“把同一处理重复应用 N 次”(每一阶以上一阶为输入):
|
* 参数(调整幅度 -400% – +400% + 迭代次数 N)按信号保存,与「均值滤波」的实现方式一致:
|
||||||
* 一阶时改定量就已严格等于「等级」对应的目标值;多阶会把相邻弱波互相抬高
|
* 同一网格内不同信号可以使用不同的增强强度,也可以分别「构建为新信号」。
|
||||||
* 彼此的基准,从而把较宽的弱波区逐步恢复。
|
|
||||||
*/
|
*/
|
||||||
int weakBoostIterations = 1;
|
std::vector<WeakPeakItem> weakPeakItems;
|
||||||
|
|
||||||
/** 参与弱波增强预览的信号条目(只包含被勾选的信号)。 */
|
/** 取某个信号的弱峰调整参数(未勾选时返回空指针)。 */
|
||||||
std::vector<WeakBoostItem> weakBoostItems;
|
const WeakPeakItem* FindWeakPeakItem(const SignalRef& ref) const;
|
||||||
|
|
||||||
/** 取某个信号的弱波增强条目(未勾选时返回空指针)。 */
|
/** 取得或创建某信号的弱峰调整参数(用于勾选)。 */
|
||||||
const WeakBoostItem* FindWeakBoostItem(const SignalRef& ref) const;
|
WeakPeakItem& EnsureWeakPeakItem(const SignalRef& ref);
|
||||||
|
|
||||||
/** 取得或创建某信号的弱波增强条目(用于勾选)。 */
|
/** 取消某信号的弱峰调整勾选。 */
|
||||||
WeakBoostItem& EnsureWeakBoostItem(const SignalRef& ref);
|
void RemoveWeakPeakItem(const SignalRef& ref);
|
||||||
|
|
||||||
/** 取消某信号的弱波增强勾选。 */
|
/** 取某信号的弱峰调整勾选状态。 */
|
||||||
void RemoveWeakBoostItem(const SignalRef& ref);
|
bool WeakPeakEnabled(const SignalRef& ref) const
|
||||||
|
|
||||||
/** 取某信号的弱波增强勾选状态。 */
|
|
||||||
bool WeakBoostEnabled(const SignalRef& ref) const
|
|
||||||
{
|
{
|
||||||
return FindWeakBoostItem(ref) != nullptr;
|
return FindWeakPeakItem(ref) != nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 弱波增强浮动面板是否展开。 */
|
/** 弱峰调整浮动面板是否展开。 */
|
||||||
bool weakBoostPanelOpen = true;
|
bool weakPeakPanelOpen = true;
|
||||||
|
|
||||||
/** 弱波增强面板左上角位置(客户区像素;<0 表示自动定位)。 */
|
/** 弱峰调整面板左上角位置(客户区像素;<0 表示自动定位)。 */
|
||||||
double weakBoostPanelX = -1.0;
|
double weakPeakPanelX = -1.0;
|
||||||
|
|
||||||
/** 弱波增强面板左上角纵坐标(<0 表示自动定位)。 */
|
/** 弱峰调整面板左上角纵坐标(<0 表示自动定位)。 */
|
||||||
double weakBoostPanelY = -1.0;
|
double weakPeakPanelY = -1.0;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -592,32 +593,49 @@ namespace jdi
|
|||||||
std::vector<float>& out);
|
std::vector<float>& out);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 「弱波增强」:把比周边更弱的纹波波峰/波谷恢复(自适应弱波增强)。
|
* @brief 「弱峰调整」:把比周边更弱的纹波波峰/波谷恢复,或(负幅度时)把弱峰削弱。
|
||||||
*
|
*
|
||||||
* 本算法**不做任何前置滤波**:需要平滑时用户可以先用「均值滤波」构建一条
|
* 本算法**不做任何前置滤波**:需要平滑时用户可以先用「均值滤波」构建一条
|
||||||
* 滤波后的信号,再对它做弱波增强。极值点直接在输入采样上识别。
|
* 滤波后的信号,再对它做弱峰调整。极值点直接在输入采样上识别。
|
||||||
|
*
|
||||||
|
* 「调整幅度」滑动条的量程是 **-400% – +400%**,它按"梯度"同时驱动**三组识别规则**:
|
||||||
|
* 越宽松的规则参与得越晚,同一滑动条位置上的幅度越小:
|
||||||
|
* | 滑动条 | 第一类(严格) | 第二类(或) | 第三类(宽松) |
|
||||||
|
* | --- | --- | --- | --- |
|
||||||
|
* | 0 – 100% | 0 – 100% | — | — |
|
||||||
|
* | 100 – 200% | 100 – 200% | 0 – 100% | — |
|
||||||
|
* | 200 – 300% | 200%(上限) | 100 – 200% | 0 – 100% |
|
||||||
|
* | 300 – 400% | 200%(上限) | 200%(上限) | 100 – 200% |
|
||||||
|
* (负值完全对称;某个极值若同时满足多组规则,取**幅度绝对值最大**的那一组。)
|
||||||
*
|
*
|
||||||
* 单阶处理(全 O(n)):
|
* 单阶处理(全 O(n)):
|
||||||
* 1. **识别趋势与极值**:扫描单调段,方向发生翻转处的上一个采样点即为
|
* 1. **识别趋势与极值**:扫描单调段,方向发生翻转处的上一个采样点即为
|
||||||
* 波峰(先升后降)或波谷(先降后升);波峰与波谷交替出现。
|
* 波峰(先升后降)或波谷(先降后升);波峰与波谷交替出现。
|
||||||
* 2. **判定“弱”**:对第 k 个波峰,取同类的左右相邻波峰(k−2、k+2)。
|
* 2. **三组识别规则**(以波峰 k 为例;`L` = 左侧相邻第一个波峰、`l` = 左侧相邻第一个波谷,
|
||||||
* 若本波峰低于两者的均值,说明它是被削弱的那个;波谷反之(高于两者均值)。
|
* `r` = 右侧相邻第一个波谷、`R` = 右侧相邻第一个波峰):
|
||||||
* 3. **目标值**(`level` = 弱波增强等级,0–1):
|
* * 第一类:`v < (L+l)/2` **且** `v < (r+R)/2`(两侧均值都要满足);
|
||||||
* * 波峰:左右两个波峰中较小者为 0%、较大者为 100%,
|
* * 第二类:`v < (L+l)/2` **或** `v < (r+R)/2`(放宽为"或");
|
||||||
* 目标 = min + level × (max − min);
|
* * 第三类:`v < L` **且** `v < R`(只与左右同类极值比较)。
|
||||||
* * 波谷:左右两个波谷中较大者为 0%、较小者为 100%,
|
* 波谷把"低于"换成"高于"、把"且/或"的上下界互换即可。
|
||||||
* 目标 = max − level × (max − min)。
|
* 3. **目标值**(每一组的幅度 `a` 都在 -2 – +2,对应其自身的 -200% – +200%):
|
||||||
|
* * `0 – +100%`:当前值 → 左右**同类**极值中**较温和**的一侧
|
||||||
|
* (波峰取较低的波峰、波谷取较高的波谷);
|
||||||
|
* * `+100 – +200%`:该侧 → 左右同类极值的**均值**;
|
||||||
|
* * `0 – -100%`:当前值 → 相邻**异类**极值中**较近**的一个
|
||||||
|
* (波峰取较高的波谷、波谷取较低的波峰);
|
||||||
|
* * `-100 – -200%`:该侧 → 相邻异类极值的**均值**。
|
||||||
|
* 削弱**不会反向增强**(目标被夹在"不越过当前值"的一侧)。
|
||||||
* 4. **平滑偏移**:极值点本身取其目标值,两侧的数据点按相邻两个极值点的偏移量
|
* 4. **平滑偏移**:极值点本身取其目标值,两侧的数据点按相邻两个极值点的偏移量
|
||||||
* 平滑过渡(平滑步进,两端导数为 0),使波形整体趋势不变、只在弱波附近被推回。
|
* 平滑过渡(平滑步进,两端导数为 0),使波形整体趋势不变、只在弱峰附近被推回。
|
||||||
*
|
*
|
||||||
* 正常周期的极值不满足条件、偏移量恒为 0,因此输出与输入逐位相同。
|
* 不属于任何一类规则点的极值偏移量恒为 0,因此输出与输入逐位相同。
|
||||||
*
|
*
|
||||||
* @param [IN] values 原始采样值。
|
* @param [IN] values 原始采样值。
|
||||||
* @param [IN] level 弱波增强等级(0–1,超出范围会被夹取)。
|
* @param [IN] amount 调整幅度(-4 – +4,对应 -400% – +400%,超出范围会被夹取;0 = 完全不动)。
|
||||||
* @param [IN] iterations 迭代次数 N(重复应用次数,至少为 1)。
|
* @param [IN] iterations 迭代次数 N(重复应用次数,至少为 1)。
|
||||||
* @param [OUT] out 输出数组(长度与 values 相同;原值为 NaN 处保持 NaN)。
|
* @param [OUT] out 输出数组(长度与 values 相同;原值为 NaN 处保持 NaN)。
|
||||||
*/
|
*/
|
||||||
void ComputeWeakWaveBoost(const std::vector<double>& values, double level, int iterations,
|
void ComputeWeakPeakAdjust(const std::vector<double>& values, double amount, int iterations,
|
||||||
std::vector<float>& out);
|
std::vector<float>& out);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
+171
-81
@@ -134,13 +134,11 @@ namespace jdi
|
|||||||
bool meanFilterPanelOpen = true;
|
bool meanFilterPanelOpen = true;
|
||||||
double meanFilterPanelX = -1.0;
|
double meanFilterPanelX = -1.0;
|
||||||
double meanFilterPanelY = -1.0;
|
double meanFilterPanelY = -1.0;
|
||||||
bool weakBoost = false;
|
bool weakPeak = false;
|
||||||
std::vector<Lane::WeakBoostItem> weakBoostItems;
|
std::vector<Lane::WeakPeakItem> weakPeakItems;
|
||||||
double weakBoostLevel = 0.5;
|
bool weakPeakPanelOpen = true;
|
||||||
int weakBoostIterations = 1;
|
double weakPeakPanelX = -1.0;
|
||||||
bool weakBoostPanelOpen = true;
|
double weakPeakPanelY = -1.0;
|
||||||
double weakBoostPanelX = -1.0;
|
|
||||||
double weakBoostPanelY = -1.0;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// 先记下每个信号原来所在网格的网格自身状态:
|
// 先记下每个信号原来所在网格的网格自身状态:
|
||||||
@@ -166,13 +164,11 @@ namespace jdi
|
|||||||
carry.meanFilterPanelOpen = lane.meanFilterPanelOpen;
|
carry.meanFilterPanelOpen = lane.meanFilterPanelOpen;
|
||||||
carry.meanFilterPanelX = lane.meanFilterPanelX;
|
carry.meanFilterPanelX = lane.meanFilterPanelX;
|
||||||
carry.meanFilterPanelY = lane.meanFilterPanelY;
|
carry.meanFilterPanelY = lane.meanFilterPanelY;
|
||||||
carry.weakBoost = lane.weakBoost;
|
carry.weakPeak = lane.weakPeak;
|
||||||
carry.weakBoostItems = lane.weakBoostItems;
|
carry.weakPeakItems = lane.weakPeakItems;
|
||||||
carry.weakBoostLevel = lane.weakBoostLevel;
|
carry.weakPeakPanelOpen = lane.weakPeakPanelOpen;
|
||||||
carry.weakBoostIterations = lane.weakBoostIterations;
|
carry.weakPeakPanelX = lane.weakPeakPanelX;
|
||||||
carry.weakBoostPanelOpen = lane.weakBoostPanelOpen;
|
carry.weakPeakPanelY = lane.weakPeakPanelY;
|
||||||
carry.weakBoostPanelX = lane.weakBoostPanelX;
|
|
||||||
carry.weakBoostPanelY = lane.weakBoostPanelY;
|
|
||||||
for (const SignalRef& ref : lane.signals)
|
for (const SignalRef& ref : lane.signals)
|
||||||
{
|
{
|
||||||
previousCarry.emplace_back(ref, carry);
|
previousCarry.emplace_back(ref, carry);
|
||||||
@@ -209,13 +205,11 @@ namespace jdi
|
|||||||
lane.meanFilterPanelOpen = carry.meanFilterPanelOpen;
|
lane.meanFilterPanelOpen = carry.meanFilterPanelOpen;
|
||||||
lane.meanFilterPanelX = carry.meanFilterPanelX;
|
lane.meanFilterPanelX = carry.meanFilterPanelX;
|
||||||
lane.meanFilterPanelY = carry.meanFilterPanelY;
|
lane.meanFilterPanelY = carry.meanFilterPanelY;
|
||||||
lane.weakBoost = carry.weakBoost;
|
lane.weakPeak = carry.weakPeak;
|
||||||
lane.weakBoostItems = carry.weakBoostItems;
|
lane.weakPeakItems = carry.weakPeakItems;
|
||||||
lane.weakBoostLevel = carry.weakBoostLevel;
|
lane.weakPeakPanelOpen = carry.weakPeakPanelOpen;
|
||||||
lane.weakBoostIterations = carry.weakBoostIterations;
|
lane.weakPeakPanelX = carry.weakPeakPanelX;
|
||||||
lane.weakBoostPanelOpen = carry.weakBoostPanelOpen;
|
lane.weakPeakPanelY = carry.weakPeakPanelY;
|
||||||
lane.weakBoostPanelX = carry.weakBoostPanelX;
|
|
||||||
lane.weakBoostPanelY = carry.weakBoostPanelY;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// 收集全部可见信号引用。
|
// 收集全部可见信号引用。
|
||||||
@@ -1052,9 +1046,9 @@ namespace jdi
|
|||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* @brief 「弱波增强」内部使用的极值点(波峰 / 波谷)。
|
* @brief 「弱峰调整」内部使用的极值点(波峰 / 波谷)。
|
||||||
*/
|
*/
|
||||||
struct BoostExtremum
|
struct PeakExtremum
|
||||||
{
|
{
|
||||||
std::size_t index = 0; /**< 采样下标。 */
|
std::size_t index = 0; /**< 采样下标。 */
|
||||||
double value = 0.0; /**< 采样值。 */
|
double value = 0.0; /**< 采样值。 */
|
||||||
@@ -1062,18 +1056,34 @@ namespace jdi
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 「弱波增强」的单阶处理:把偏弱的波峰上拉、偏弱的波谷下拉。
|
* @brief 三组识别规则各自的调整幅度(每一组都在 -2 – +2,即面板上的 -200% – +200%)。
|
||||||
|
*
|
||||||
|
* 滑动条的 ±400% 会按"梯度"同时驱动三组规则(见 `ComputeWeakPeakAdjust`):
|
||||||
|
* 越宽松的规则参与得越晚、在同一滑动条位置上的幅度越小。
|
||||||
|
*/
|
||||||
|
struct WeakPeakAmounts
|
||||||
|
{
|
||||||
|
double first = 0.0; /**< 第一类(严格:两侧均值都要满足)。 */
|
||||||
|
double second = 0.0; /**< 第二类(其中一侧满足即可)。 */
|
||||||
|
double third = 0.0; /**< 第三类(更宽松:只与左右同类极值比较)。 */
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 「弱峰调整」的单阶处理:把偏弱的波峰上拉 / 偏弱的波谷下拉,
|
||||||
|
* 或(负幅度)把弱峰本身削弱、压平。
|
||||||
*
|
*
|
||||||
* **不做任何前置滤波**(需要平滑时用户可以先用「均值滤波」构建一条滤波后的信号):
|
* **不做任何前置滤波**(需要平滑时用户可以先用「均值滤波」构建一条滤波后的信号):
|
||||||
* 极值点直接在输入采样上通过单调段的方向翻转识别。
|
* 极值点直接在输入采样上通过单调段的方向翻转识别。三组识别规则(见 `WeakPeakAmounts`)
|
||||||
|
* 各自有自己的幅度;某个极值若同时满足多组规则,取**幅度绝对值最大**的那一组
|
||||||
|
*(= 最严格的那一组),这样"越严格越先被推到目标值"。
|
||||||
*
|
*
|
||||||
* @param [IN] input 本阶的输入采样值。
|
* @param [IN] input 本阶的输入采样值。
|
||||||
* @param [IN] strength 弱波增强等级(0–1,已夹取)。
|
* @param [IN] amounts 三组规则的调整幅度(各 -2 – +2,已夹取;0 = 该组不动)。
|
||||||
* @param [OUT] output 本阶的输出(长度与 input 相同;原值为 NaN 处保持 NaN)。
|
* @param [OUT] output 本阶的输出(长度与 input 相同;原值为 NaN 处保持 NaN)。
|
||||||
* @return 确实有采样点被改动返回 true;完全无弱波时返回 false。
|
* @return 确实有采样点被改动返回 true;没有任何点需要调整时返回 false。
|
||||||
*/
|
*/
|
||||||
bool ApplyWeakBoostStage(const std::vector<float>& input, double strength,
|
bool ApplyWeakPeakStage(const std::vector<float>& input, const WeakPeakAmounts& amounts,
|
||||||
std::vector<float>& output, std::vector<BoostExtremum>& extremes)
|
std::vector<float>& output, std::vector<PeakExtremum>& extremes)
|
||||||
{
|
{
|
||||||
const std::size_t count = input.size();
|
const std::size_t count = input.size();
|
||||||
output.assign(count, std::numeric_limits<float>::quiet_NaN());
|
output.assign(count, std::numeric_limits<float>::quiet_NaN());
|
||||||
@@ -1127,7 +1137,7 @@ namespace jdi
|
|||||||
else if (step != direction)
|
else if (step != direction)
|
||||||
{
|
{
|
||||||
// 方向在此翻转:上一个采样点就是极值(原方向向上则为波峰)。
|
// 方向在此翻转:上一个采样点就是极值(原方向向上则为波峰)。
|
||||||
BoostExtremum extreme;
|
PeakExtremum extreme;
|
||||||
extreme.index = previousIndex;
|
extreme.index = previousIndex;
|
||||||
extreme.value = previousValue;
|
extreme.value = previousValue;
|
||||||
extreme.type = direction;
|
extreme.type = direction;
|
||||||
@@ -1146,16 +1156,16 @@ namespace jdi
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2) 逐个极值点判定“是否偏弱”,并算出它自己的目标偏移量。
|
// 2) 逐个极值点判定它属于哪几类规则点,并算出它自己的目标偏移量。
|
||||||
const std::size_t extremeCount = extremes.size();
|
const std::size_t extremeCount = extremes.size();
|
||||||
thread_local std::vector<double> shiftBuffer;
|
thread_local std::vector<double> shiftBuffer;
|
||||||
std::vector<double>& shift = shiftBuffer;
|
std::vector<double>& shift = shiftBuffer;
|
||||||
shift.assign(extremeCount, 0.0);
|
shift.assign(extremeCount, 0.0);
|
||||||
for (std::size_t k = 2; k + 2 < extremeCount; ++k)
|
for (std::size_t k = 2; k + 2 < extremeCount; ++k)
|
||||||
{
|
{
|
||||||
const BoostExtremum& current = extremes[k];
|
const PeakExtremum& current = extremes[k];
|
||||||
const BoostExtremum& left = extremes[k - 2];
|
const PeakExtremum& left = extremes[k - 2];
|
||||||
const BoostExtremum& right = extremes[k + 2];
|
const PeakExtremum& right = extremes[k + 2];
|
||||||
if (left.type != current.type || right.type != current.type)
|
if (left.type != current.type || right.type != current.type)
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
@@ -1165,27 +1175,93 @@ namespace jdi
|
|||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
const double mean = (left.value + right.value) * 0.5;
|
// 相邻的**异类**极值(波峰旁边的波谷 / 波谷旁边的波峰)。
|
||||||
const double lower = std::min(left.value, right.value);
|
const PeakExtremum& nearLeft = extremes[k - 1];
|
||||||
const double upper = std::max(left.value, right.value);
|
const PeakExtremum& nearRight = extremes[k + 1];
|
||||||
double destination = 0.0;
|
if (nearLeft.type == current.type || nearRight.type == current.type)
|
||||||
if (current.type > 0)
|
|
||||||
{
|
{
|
||||||
// 波峰:低于左右两个波峰的均值 → 判定为弱波,上拉到等级对应的目标值。
|
continue; // 极值序列本应交替;异常时不动这一点
|
||||||
if (!(current.value < mean))
|
}
|
||||||
|
if (!std::isfinite(nearLeft.value) || !std::isfinite(nearRight.value))
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
destination = lower + strength * (upper - lower);
|
const bool isPeak = (current.type > 0);
|
||||||
|
// 「左侧/右侧相邻的一个同类极值 + 一个异类极值」的均值。
|
||||||
|
const double leftMean = (left.value + nearLeft.value) * 0.5;
|
||||||
|
const double rightMean = (nearRight.value + right.value) * 0.5;
|
||||||
|
// 同类极值里"较温和"的一侧:波峰取较低者、波谷取较高者。
|
||||||
|
const double milderSame = isPeak ? std::min(left.value, right.value)
|
||||||
|
: std::max(left.value, right.value);
|
||||||
|
const double meanSame = (left.value + right.value) * 0.5;
|
||||||
|
// 异类极值里"较近"的一个:波峰取较高的波谷、波谷取较低的波峰。
|
||||||
|
const double nearerOpposite = isPeak ? std::max(nearLeft.value, nearRight.value)
|
||||||
|
: std::min(nearLeft.value, nearRight.value);
|
||||||
|
const double meanOpposite = (nearLeft.value + nearRight.value) * 0.5;
|
||||||
|
|
||||||
|
// 三组识别规则(以弱峰为例,弱谷把"低于"换成"高于"、max/min 互换):
|
||||||
|
// 第一类:**同时**低于左右两侧的(相邻同类极值 + 相邻异类极值)均值;
|
||||||
|
// 第二类:低于其中**任意一侧**的该均值(从"且"放宽为"或");
|
||||||
|
// 第三类:只与左右**同类**极值比较 —— 低于左右两个同类极值即算弱峰。
|
||||||
|
const bool firstClass = isPeak ? ((current.value < leftMean) && (current.value < rightMean))
|
||||||
|
: ((current.value > leftMean) && (current.value > rightMean));
|
||||||
|
const bool secondClass = isPeak ? ((current.value < leftMean) || (current.value < rightMean))
|
||||||
|
: ((current.value > leftMean) || (current.value > rightMean));
|
||||||
|
const bool thirdClass = isPeak ? ((current.value < left.value) && (current.value < right.value))
|
||||||
|
: ((current.value > left.value) && (current.value > right.value));
|
||||||
|
|
||||||
|
// 同时满足多组时取**幅度绝对值最大**的那一组(= 最严格的那一组先被推到目标值)。
|
||||||
|
double amount = 0.0;
|
||||||
|
if (firstClass)
|
||||||
|
{
|
||||||
|
amount = amounts.first;
|
||||||
|
}
|
||||||
|
if (secondClass && std::fabs(amounts.second) > std::fabs(amount))
|
||||||
|
{
|
||||||
|
amount = amounts.second;
|
||||||
|
}
|
||||||
|
if (thirdClass && std::fabs(amounts.third) > std::fabs(amount))
|
||||||
|
{
|
||||||
|
amount = amounts.third;
|
||||||
|
}
|
||||||
|
if (amount == 0.0)
|
||||||
|
{
|
||||||
|
continue; // 不需要调整
|
||||||
|
}
|
||||||
|
|
||||||
|
double destination = current.value;
|
||||||
|
if (amount > 0.0)
|
||||||
|
{
|
||||||
|
// ---- 正幅度(增强)----
|
||||||
|
// 0 – 100%:当前值 → 左右**同类**极值中较温和的一侧(波峰取较低者、波谷取较高者);
|
||||||
|
// 100 – 200%:该侧 → 左右**同类**极值的**均值**。
|
||||||
|
// ⚠️ 波峰从 `milderSame` 走向 `meanSame`(向上)、波谷同样从 `milderSame`
|
||||||
|
// 走向 `meanSame`(此时 `meanSame` 可能比 `milderSame` **低**),
|
||||||
|
// 即两者的插值方向一致(都是"milderSame → meanSame"),无需分别取符号。
|
||||||
|
destination = (amount <= 1.0)
|
||||||
|
? (current.value + amount * (milderSame - current.value))
|
||||||
|
: (milderSame + (amount - 1.0) * (meanSame - milderSame));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// 波谷:高于左右两个波谷的均值 → 判定为弱波,下拉到等级对应的目标值。
|
// ---- 负幅度(削弱)----
|
||||||
if (!(current.value > mean))
|
// 0 – -100%:当前值 → 相邻**异类**极值中较近的一个
|
||||||
|
// (波峰取较高的波谷、波谷取较低的波峰);
|
||||||
|
// -100 – -200%:该侧 → 相邻异类极值的**均值**。
|
||||||
|
const double magnitude = std::min(2.0, -amount);
|
||||||
|
destination = (magnitude <= 1.0)
|
||||||
|
? (current.value + magnitude * (nearerOpposite - current.value))
|
||||||
|
: (nearerOpposite +
|
||||||
|
(magnitude - 1.0) * (meanOpposite - nearerOpposite));
|
||||||
|
// ⚠️ 削弱**不得反向增强**:目标被夹在"不越过当前值"的一侧。
|
||||||
|
if (isPeak)
|
||||||
{
|
{
|
||||||
continue;
|
destination = std::min(destination, current.value);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
destination = std::max(destination, current.value);
|
||||||
}
|
}
|
||||||
destination = upper - strength * (upper - lower);
|
|
||||||
}
|
}
|
||||||
shift[k] = destination - current.value;
|
shift[k] = destination - current.value;
|
||||||
}
|
}
|
||||||
@@ -1235,7 +1311,7 @@ namespace jdi
|
|||||||
}
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
void ComputeWeakWaveBoost(const std::vector<double>& values, double level, int iterations,
|
void ComputeWeakPeakAdjust(const std::vector<double>& values, double amount, int iterations,
|
||||||
std::vector<float>& out)
|
std::vector<float>& out)
|
||||||
{
|
{
|
||||||
const std::size_t count = values.size();
|
const std::size_t count = values.size();
|
||||||
@@ -1247,10 +1323,10 @@ namespace jdi
|
|||||||
|
|
||||||
// 第 1 阶:从原始采样值出发(非有限值保持 NaN)。
|
// 第 1 阶:从原始采样值出发(非有限值保持 NaN)。
|
||||||
// 工作缓冲用线程本地变量复用容量:单条曲线动辄上千万采样,若每次调用都重新分配
|
// 工作缓冲用线程本地变量复用容量:单条曲线动辄上千万采样,若每次调用都重新分配
|
||||||
// 并零初始化,单条「弱波增强」的耗时会被明显放大(内存分配 + page fault)。
|
// 并零初始化,单条「弱峰调整」的耗时会被明显放大(内存分配 + page fault)。
|
||||||
thread_local std::vector<float> currentBuffer;
|
thread_local std::vector<float> currentBuffer;
|
||||||
thread_local std::vector<float> nextBuffer;
|
thread_local std::vector<float> nextBuffer;
|
||||||
thread_local std::vector<BoostExtremum> extremesBuffer;
|
thread_local std::vector<PeakExtremum> extremesBuffer;
|
||||||
std::vector<float>& current = currentBuffer;
|
std::vector<float>& current = currentBuffer;
|
||||||
current.assign(count, std::numeric_limits<float>::quiet_NaN());
|
current.assign(count, std::numeric_limits<float>::quiet_NaN());
|
||||||
for (std::size_t index = 0; index < count; ++index)
|
for (std::size_t index = 0; index < count; ++index)
|
||||||
@@ -1263,14 +1339,27 @@ namespace jdi
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 「迭代次数 N」与「均值滤波」的迭代次数语义一致:把同一处理**重复应用 N 次**,
|
// 「迭代次数 N」与「均值滤波」的迭代次数语义一致:把同一处理**重复应用 N 次**,
|
||||||
// 每一阶以上一阶的结果为输入。一阶时改定量已严格等于「等级」对应的目标值;
|
// 每一阶以上一阶的结果为输入。一阶时改定量已严格等于「幅度」对应的目标值;
|
||||||
// 多次迭代时相邻的弱波会互相抬高彼此的基准,从而把较宽的弱波区逐步恢复。
|
// 多次迭代时相邻的弱峰会互相抬高彼此的基准,从而把较宽的弱峰区逐步恢复(或削弱)。
|
||||||
const int stages = std::max(1, std::min(iterations, 1000));
|
const int stages = std::max(1, std::min(iterations, 1000));
|
||||||
const double strength = std::max(0.0, std::min(1.0, level));
|
// 滑动条量程 ±400%(`amount` = 百分比 / 100,即 -4 – +4),按"梯度"同时驱动三组规则:
|
||||||
|
// 第一类:始终参与,幅度 = clamp(|a|, 0, 2);
|
||||||
|
// 第二类:|a| > 100% 起参与,幅度 = clamp(|a| - 1, 0, 2);
|
||||||
|
// 第三类:|a| > 200% 起参与,幅度 = clamp(|a| - 2, 0, 2)。
|
||||||
|
// 于是滑动条 0–100% 只有第一类在动;100–200% 第一类走完 100–200%、第二类开始走 0–100%;
|
||||||
|
// 200–300% 第一类已到上限、第二类走 100–200%、第三类开始走 0–100%;
|
||||||
|
// 300–400% 前两类都已到上限,只有第三类在走 100–200%。负值完全对称。
|
||||||
|
const double clamped = std::max(-4.0, std::min(4.0, amount));
|
||||||
|
const double magnitude = std::fabs(clamped);
|
||||||
|
const double sign = (clamped < 0.0) ? -1.0 : 1.0;
|
||||||
|
WeakPeakAmounts amounts;
|
||||||
|
amounts.first = sign * std::min(magnitude, 2.0);
|
||||||
|
amounts.second = sign * std::max(0.0, std::min(2.0, magnitude - 1.0));
|
||||||
|
amounts.third = sign * std::max(0.0, std::min(2.0, magnitude - 2.0));
|
||||||
std::vector<float>& next = nextBuffer;
|
std::vector<float>& next = nextBuffer;
|
||||||
for (int stage = 0; stage < stages; ++stage)
|
for (int stage = 0; stage < stages; ++stage)
|
||||||
{
|
{
|
||||||
if (!ApplyWeakBoostStage(current, strength, next, extremesBuffer))
|
if (!ApplyWeakPeakStage(current, amounts, next, extremesBuffer))
|
||||||
{
|
{
|
||||||
// 这一阶没有任何采样点需要调整:后续迭代次数也不会再有变化,提前结束。
|
// 这一阶没有任何采样点需要调整:后续迭代次数也不会再有变化,提前结束。
|
||||||
break;
|
break;
|
||||||
@@ -1350,9 +1439,9 @@ namespace jdi
|
|||||||
edgeCenterItems.end());
|
edgeCenterItems.end());
|
||||||
}
|
}
|
||||||
|
|
||||||
const Lane::WeakBoostItem* Lane::FindWeakBoostItem(const SignalRef& ref) const
|
const Lane::WeakPeakItem* Lane::FindWeakPeakItem(const SignalRef& ref) const
|
||||||
{
|
{
|
||||||
for (const WeakBoostItem& item : weakBoostItems)
|
for (const WeakPeakItem& item : weakPeakItems)
|
||||||
{
|
{
|
||||||
if (item.ref == ref)
|
if (item.ref == ref)
|
||||||
{
|
{
|
||||||
@@ -1362,27 +1451,27 @@ namespace jdi
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
Lane::WeakBoostItem& Lane::EnsureWeakBoostItem(const SignalRef& ref)
|
Lane::WeakPeakItem& Lane::EnsureWeakPeakItem(const SignalRef& ref)
|
||||||
{
|
{
|
||||||
for (WeakBoostItem& item : weakBoostItems)
|
for (WeakPeakItem& item : weakPeakItems)
|
||||||
{
|
{
|
||||||
if (item.ref == ref)
|
if (item.ref == ref)
|
||||||
{
|
{
|
||||||
return item;
|
return item;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
WeakBoostItem item;
|
WeakPeakItem item;
|
||||||
item.ref = ref;
|
item.ref = ref;
|
||||||
weakBoostItems.push_back(item);
|
weakPeakItems.push_back(item);
|
||||||
return weakBoostItems.back();
|
return weakPeakItems.back();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Lane::RemoveWeakBoostItem(const SignalRef& ref)
|
void Lane::RemoveWeakPeakItem(const SignalRef& ref)
|
||||||
{
|
{
|
||||||
weakBoostItems.erase(
|
weakPeakItems.erase(
|
||||||
std::remove_if(weakBoostItems.begin(), weakBoostItems.end(),
|
std::remove_if(weakPeakItems.begin(), weakPeakItems.end(),
|
||||||
[&ref](const WeakBoostItem& item) { return item.ref == ref; }),
|
[&ref](const WeakPeakItem& item) { return item.ref == ref; }),
|
||||||
weakBoostItems.end());
|
weakPeakItems.end());
|
||||||
}
|
}
|
||||||
|
|
||||||
int FindNearestSample(const DataFile& file, double time)
|
int FindNearestSample(const DataFile& file, double time)
|
||||||
@@ -1609,17 +1698,20 @@ namespace jdi
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 均值滤波 / 中心轴边沿计数的条目同样持有信号引用,必须一起删除/前移。
|
// 均值滤波 / 中心轴边沿计数 / 弱峰调整的条目同样持有信号引用,必须一起删除/前移。
|
||||||
const auto purgeRefs = [fileIndex, signalIndex](std::vector<Lane::MeanFilterItem>& items)
|
// 三类条目都是「带 `ref` 的结构体」,因此用泛型 lambda 统一处理
|
||||||
|
//(弱峰调整的参数与均值滤波不同,不能再共用同一个具体类型)。
|
||||||
|
const auto purgeRefs = [fileIndex, signalIndex](auto& items)
|
||||||
{
|
{
|
||||||
|
using ItemType = typename std::decay_t<decltype(items)>::value_type;
|
||||||
items.erase(std::remove_if(items.begin(), items.end(),
|
items.erase(std::remove_if(items.begin(), items.end(),
|
||||||
[fileIndex, signalIndex](const Lane::MeanFilterItem& item)
|
[fileIndex, signalIndex](const ItemType& item)
|
||||||
{
|
{
|
||||||
return item.ref.fileIndex == fileIndex &&
|
return item.ref.fileIndex == fileIndex &&
|
||||||
item.ref.signalIndex == signalIndex;
|
item.ref.signalIndex == signalIndex;
|
||||||
}),
|
}),
|
||||||
items.end());
|
items.end());
|
||||||
for (Lane::MeanFilterItem& item : items)
|
for (ItemType& item : items)
|
||||||
{
|
{
|
||||||
if (item.ref.fileIndex == fileIndex && item.ref.signalIndex > signalIndex)
|
if (item.ref.fileIndex == fileIndex && item.ref.signalIndex > signalIndex)
|
||||||
{
|
{
|
||||||
@@ -1629,7 +1721,7 @@ namespace jdi
|
|||||||
};
|
};
|
||||||
purgeRefs(lane.meanFilterItems);
|
purgeRefs(lane.meanFilterItems);
|
||||||
purgeRefs(lane.edgeCenterItems);
|
purgeRefs(lane.edgeCenterItems);
|
||||||
purgeRefs(lane.weakBoostItems);
|
purgeRefs(lane.weakPeakItems);
|
||||||
}
|
}
|
||||||
// 被移除信号若独占了某个网格,该网格随之消失(与取消勾选的行为一致)。
|
// 被移除信号若独占了某个网格,该网格随之消失(与取消勾选的行为一致)。
|
||||||
m_lanes.erase(std::remove_if(m_lanes.begin(), m_lanes.end(),
|
m_lanes.erase(std::remove_if(m_lanes.begin(), m_lanes.end(),
|
||||||
@@ -1921,20 +2013,18 @@ namespace jdi
|
|||||||
lane.edgeCenterPanelOpen = source.edgeCenterPanelOpen;
|
lane.edgeCenterPanelOpen = source.edgeCenterPanelOpen;
|
||||||
lane.edgeCenterPanelX = source.edgeCenterPanelX;
|
lane.edgeCenterPanelX = source.edgeCenterPanelX;
|
||||||
lane.edgeCenterPanelY = source.edgeCenterPanelY;
|
lane.edgeCenterPanelY = source.edgeCenterPanelY;
|
||||||
lane.weakBoost = source.weakBoost;
|
lane.weakPeak = source.weakPeak;
|
||||||
lane.weakBoostLevel = source.weakBoostLevel;
|
lane.weakPeakPanelOpen = source.weakPeakPanelOpen;
|
||||||
lane.weakBoostIterations = source.weakBoostIterations;
|
lane.weakPeakPanelX = source.weakPeakPanelX;
|
||||||
lane.weakBoostPanelOpen = source.weakBoostPanelOpen;
|
lane.weakPeakPanelY = source.weakPeakPanelY;
|
||||||
lane.weakBoostPanelX = source.weakBoostPanelX;
|
|
||||||
lane.weakBoostPanelY = source.weakBoostPanelY;
|
|
||||||
lane.meanFilter = source.meanFilter;
|
lane.meanFilter = source.meanFilter;
|
||||||
lane.meanFilterPanelOpen = source.meanFilterPanelOpen;
|
lane.meanFilterPanelOpen = source.meanFilterPanelOpen;
|
||||||
lane.meanFilterPanelX = source.meanFilterPanelX;
|
lane.meanFilterPanelX = source.meanFilterPanelX;
|
||||||
lane.meanFilterPanelY = source.meanFilterPanelY;
|
lane.meanFilterPanelY = source.meanFilterPanelY;
|
||||||
// 拆分后每个子网格只保留自己那个信号的均值滤波 / 中心轴 / 弱波增强条目。
|
// 拆分后每个子网格只保留自己那个信号的均值滤波 / 中心轴 / 弱峰调整条目。
|
||||||
lane.meanFilterItems.clear();
|
lane.meanFilterItems.clear();
|
||||||
lane.edgeCenterItems.clear();
|
lane.edgeCenterItems.clear();
|
||||||
lane.weakBoostItems.clear();
|
lane.weakPeakItems.clear();
|
||||||
for (const Lane::MeanFilterItem& item : source.meanFilterItems)
|
for (const Lane::MeanFilterItem& item : source.meanFilterItems)
|
||||||
{
|
{
|
||||||
if (item.ref == ref)
|
if (item.ref == ref)
|
||||||
@@ -1949,11 +2039,11 @@ namespace jdi
|
|||||||
lane.edgeCenterItems.push_back(item);
|
lane.edgeCenterItems.push_back(item);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (const Lane::WeakBoostItem& item : source.weakBoostItems)
|
for (const Lane::WeakPeakItem& item : source.weakPeakItems)
|
||||||
{
|
{
|
||||||
if (item.ref == ref)
|
if (item.ref == ref)
|
||||||
{
|
{
|
||||||
lane.weakBoostItems.push_back(item);
|
lane.weakPeakItems.push_back(item);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
lane.signals.push_back(ref);
|
lane.signals.push_back(ref);
|
||||||
@@ -2010,7 +2100,7 @@ namespace jdi
|
|||||||
++item.ref.signalIndex;
|
++item.ref.signalIndex;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (Lane::WeakBoostItem& item : lane.weakBoostItems)
|
for (Lane::WeakPeakItem& item : lane.weakPeakItems)
|
||||||
{
|
{
|
||||||
if (item.ref.fileIndex == fileIndex && item.ref.signalIndex >= insertPos)
|
if (item.ref.fileIndex == fileIndex && item.ref.signalIndex >= insertPos)
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in new issue
Block a user