feat(resample): 实验性功能新增「重采样」(位于「自由标记」上方)
- Lane::ResampleItem + resample/resampleItems/resamplePanel/resampleScroll, 并在 LaneCarry / SplitLane / PruneLaneSignalItems / RemapAfterSignalInsert|Removal 同步 - 面板:可滚动分组列表(信号色条目栏 + 复选框 + 展开三角),展开后 4 行: 最大/最小数据间隔(只读,勾选时一次性缓存,绘制不扫描采样)+ 重采样间隔/频率(输入) - 输入框联动:改间隔同步频率,改频率同步间隔(输入单位分别为秒 / Hz) - 「构建为新信号」:DataSet::AppendGroupAxis 追加新时间轴 + InsertDerivedSignal, 采样值先按公式/系数/绝对值/镜像/Y偏移变换再等间隔线性插值,时间轴偏移不烘值 - 新信号名 <原信号> (重采样 <自适应单位频率>);Utf8Util::FormatFrequency 为唯一实现 - 非单调轴 / 点太少 / 间隔过大 / 点数超 2000 万 ⇒ 拒绝构建并给出自绘提示 - 顺带加入 FormulaEval(公式编译/求值,供 ApplyDrawTransform 与后续 UI 使用)
This commit is contained in:
1 parent
30e9d65c30
commit
1e1c2432ae
19 files changed
+1979
-56
No files matched your search
@@ -28,12 +28,13 @@ namespace jdi
|
||||
const bool centerAxis = (kind == EdgePanelKind::CENTER);
|
||||
const bool meanFilter = (kind == EdgePanelKind::MEAN);
|
||||
const bool weakPeak = (kind == EdgePanelKind::PEAK);
|
||||
// 「均值滤波」「中心轴边沿计数」「弱峰调整」= 可滚动的分组列表
|
||||
const bool resamplePanel = (kind == EdgePanelKind::RESAMPLE);
|
||||
// 「均值滤波」「中心轴边沿计数」「弱峰调整」「重采样」= 可滚动的分组列表
|
||||
//(信号色叠底条目栏 + 右侧复选框 + 展开后的参数行);
|
||||
// **「边沿计数」= 表格**(表头:信号名 / 上升沿计数 / 下降沿计数)。
|
||||
// ⚠️ 旧式「逐条目」排版(无滚动的名称+参数行)已彻底删除:上百个信号时它会算出
|
||||
// 上万像素高的面板,超出绘图区后**面板像素缓存一次都存不进去** ⇒ 每帧整块重画。
|
||||
const bool listStyle = (centerAxis || meanFilter || weakPeak);
|
||||
const bool listStyle = (centerAxis || meanFilter || weakPeak || resamplePanel);
|
||||
const bool countTable = (kind == EdgePanelKind::COUNT);
|
||||
|
||||
// 标题行 = “GX” 徽标 + 标题(“边沿计数” / “中心轴边沿计数” / “均值滤波” / “弱峰调整”
|
||||
@@ -41,7 +42,10 @@ namespace jdi
|
||||
const std::wstring title =
|
||||
meanFilter ? std::wstring(L"均值滤波")
|
||||
: (weakPeak ? std::wstring(kWeakPeakTitle)
|
||||
: ((centerAxis ? L"中心轴边沿计数" : L"边沿计数") + EdgeRangeSuffix()));
|
||||
: (resamplePanel
|
||||
? std::wstring(kResampleTitle)
|
||||
: ((centerAxis ? L"中心轴边沿计数" : L"边沿计数") +
|
||||
EdgeRangeSuffix())));
|
||||
float width = kEdgePanelPad * 2.0f * m_scale + kLaneBadgeWidth * m_scale +
|
||||
8.0f * m_scale + UiMeasureTextWidth(m_fonts->uiSmall, title) +
|
||||
kEdgePanelTitleHeight * m_scale;
|
||||
@@ -162,6 +166,22 @@ namespace jdi
|
||||
kWeakPeakResetGap * m_scale + kWeakPeakResetButtonSize * m_scale;
|
||||
width = std::max(width, kEdgePanelPad * 2.0f * m_scale + sliderRowWidth +
|
||||
(kFreeMarkerScrollWidth + 4.0f) * m_scale);
|
||||
if (resamplePanel)
|
||||
{
|
||||
// 「重采样」四行 = 「标签列 + 数值列」(两行统计 + 两行输入框),
|
||||
// 标签取四者中最宽的一个,数值列按间隔 / 频率的典型文本宽度取。
|
||||
const float resampleLabelColumn =
|
||||
std::max(std::max(UiMeasureTextWidth(m_fonts->uiSmall, kResampleMaxIntervalLabel),
|
||||
UiMeasureTextWidth(m_fonts->uiSmall, kResampleMinIntervalLabel)),
|
||||
std::max(UiMeasureTextWidth(m_fonts->uiSmall, kResampleIntervalLabel),
|
||||
UiMeasureTextWidth(m_fonts->uiSmall, kResampleFrequencyLabel)));
|
||||
const float resampleValueColumn =
|
||||
std::max(UiMeasureTextWidth(m_fonts->monoSmall, L"1.23456 GHz"),
|
||||
UiMeasureTextWidth(m_fonts->monoSmall, L"1h 1min 2s"));
|
||||
width = std::max(width, kEdgePanelPad * 2.0f * m_scale + resampleLabelColumn +
|
||||
8.0f * m_scale + resampleValueColumn +
|
||||
(kFreeMarkerScrollWidth + 4.0f) * m_scale);
|
||||
}
|
||||
}
|
||||
|
||||
if (weakPeak)
|
||||
@@ -174,9 +194,9 @@ namespace jdi
|
||||
width = std::max(width, kEdgePanelPad * 2.0f * m_scale + rowWidth);
|
||||
}
|
||||
|
||||
if (meanFilter || weakPeak)
|
||||
if (meanFilter || weakPeak || resamplePanel)
|
||||
{
|
||||
// 底部按钮行 = 「构建为新信号」(仅均值滤波 / 弱峰调整有),按按钮文字实测宽度确定下限。
|
||||
// 底部按钮行 = 「构建为新信号」(均值滤波 / 弱峰调整 / 重采样有),按按钮文字实测宽度确定下限。
|
||||
const float buttonWidth =
|
||||
UiMeasureTextWidth(m_fonts->uiSmall, kBuildSignalLabel) + 28.0f * m_scale;
|
||||
width = std::max(width, kEdgePanelPad * 2.0f * m_scale + buttonWidth);
|
||||
@@ -276,21 +296,26 @@ namespace jdi
|
||||
const bool meanFilter = (kind == EdgePanelKind::MEAN);
|
||||
const bool weakPeak = (kind == EdgePanelKind::PEAK);
|
||||
const bool freeMarker = (kind == EdgePanelKind::MARK);
|
||||
// 「均值滤波」「中心轴边沿计数」「弱峰调整」共用同一套**可滚动分组列表**排布
|
||||
const bool resamplePanel = (kind == EdgePanelKind::RESAMPLE);
|
||||
// 「均值滤波」「中心轴边沿计数」「弱峰调整」「重采样」共用同一套**可滚动分组列表**排布
|
||||
//(信号色叠底条目栏 + 展开后的参数行);「边沿计数」自 2026-11-02 起走**表格**分支。
|
||||
const bool listStyle = (meanFilter || centerAxis || weakPeak);
|
||||
const bool listStyle = (meanFilter || centerAxis || weakPeak || resamplePanel);
|
||||
const bool countTable = (kind == EdgePanelKind::COUNT);
|
||||
const bool hasButton = (meanFilter || weakPeak);
|
||||
const bool hasButton = (meanFilter || weakPeak || resamplePanel);
|
||||
const bool enabled = meanFilter ? lane.meanFilter
|
||||
: (weakPeak ? lane.weakPeak
|
||||
: (freeMarker ? lane.freeMarker
|
||||
: (centerAxis ? lane.edgeCenter
|
||||
: lane.edgeCount)));
|
||||
: (resamplePanel ? lane.resample
|
||||
: (centerAxis ? lane.edgeCenter
|
||||
: lane.edgeCount))));
|
||||
const bool open = meanFilter ? lane.meanFilterPanel.open
|
||||
: (weakPeak ? lane.weakPeakPanel.open
|
||||
: (freeMarker ? lane.freeMarkerPanel.open
|
||||
: (centerAxis ? lane.edgeCenterPanel.open
|
||||
: lane.edgePanel.open)));
|
||||
: (resamplePanel
|
||||
? lane.resamplePanel.open
|
||||
: (centerAxis
|
||||
? lane.edgeCenterPanel.open
|
||||
: lane.edgePanel.open))));
|
||||
if (!enabled || !open)
|
||||
{
|
||||
return layout;
|
||||
@@ -299,9 +324,11 @@ namespace jdi
|
||||
const bool collapsed = meanFilter ? lane.meanFilterPanel.collapsed
|
||||
: (weakPeak ? lane.weakPeakPanel.collapsed
|
||||
: (freeMarker ? lane.freeMarkerPanel.collapsed
|
||||
: (centerAxis
|
||||
? lane.edgeCenterPanel.collapsed
|
||||
: lane.edgePanel.collapsed)));
|
||||
: (resamplePanel
|
||||
? lane.resamplePanel.collapsed
|
||||
: (centerAxis
|
||||
? lane.edgeCenterPanel.collapsed
|
||||
: lane.edgePanel.collapsed))));
|
||||
layout.collapsed = collapsed;
|
||||
|
||||
int rowCount = 0;
|
||||
@@ -368,7 +395,12 @@ namespace jdi
|
||||
}
|
||||
content += groupRow;
|
||||
bool expanded = false;
|
||||
if (weakPeak)
|
||||
if (resamplePanel)
|
||||
{
|
||||
const Lane::ResampleItem* item = lane.FindResampleItem(ref);
|
||||
expanded = (item != nullptr && !item->collapsed);
|
||||
}
|
||||
else if (weakPeak)
|
||||
{
|
||||
const Lane::WeakPeakItem* item = lane.FindWeakPeakItem(ref);
|
||||
expanded = (item != nullptr && !item->collapsed);
|
||||
@@ -385,10 +417,18 @@ namespace jdi
|
||||
}
|
||||
if (expanded)
|
||||
{
|
||||
content += sliderRow * 2.0f;
|
||||
if (centerAxis)
|
||||
if (resamplePanel)
|
||||
{
|
||||
content += groupRow; // 统计行(上升沿 / 下降沿)
|
||||
// 四行:最大间隔 / 最小间隔 / 重采样间隔 / 重采样频率。
|
||||
content += sliderRow * 4.0f;
|
||||
}
|
||||
else
|
||||
{
|
||||
content += sliderRow * 2.0f;
|
||||
if (centerAxis)
|
||||
{
|
||||
content += groupRow; // 统计行(上升沿 / 下降沿)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -489,13 +529,19 @@ namespace jdi
|
||||
const double storedX = meanFilter ? lane.meanFilterPanel.x
|
||||
: (weakPeak ? lane.weakPeakPanel.x
|
||||
: (freeMarker ? lane.freeMarkerPanel.x
|
||||
: (centerAxis ? lane.edgeCenterPanel.x
|
||||
: lane.edgePanel.x)));
|
||||
: (resamplePanel
|
||||
? lane.resamplePanel.x
|
||||
: (centerAxis
|
||||
? lane.edgeCenterPanel.x
|
||||
: lane.edgePanel.x))));
|
||||
const double storedY = meanFilter ? lane.meanFilterPanel.y
|
||||
: (weakPeak ? lane.weakPeakPanel.y
|
||||
: (freeMarker ? lane.freeMarkerPanel.y
|
||||
: (centerAxis ? lane.edgeCenterPanel.y
|
||||
: lane.edgePanel.y)));
|
||||
: (resamplePanel
|
||||
? lane.resamplePanel.y
|
||||
: (centerAxis
|
||||
? lane.edgeCenterPanel.y
|
||||
: lane.edgePanel.y))));
|
||||
int openPanelOrdinal = 0;
|
||||
{
|
||||
// ⚠️ 错开量用「**当前真正显示出来的面板**中排在本面板之前的个数」:
|
||||
@@ -519,12 +565,14 @@ namespace jdi
|
||||
return lane.weakPeak && lane.weakPeakPanel.open;
|
||||
case EdgePanelKind::MARK:
|
||||
return lane.freeMarker && lane.freeMarkerPanel.open;
|
||||
case EdgePanelKind::RESAMPLE:
|
||||
return lane.resample && lane.resamplePanel.open;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
static const EdgePanelKind kOrder[5] = {EdgePanelKind::COUNT, EdgePanelKind::CENTER,
|
||||
EdgePanelKind::MEAN, EdgePanelKind::PEAK,
|
||||
EdgePanelKind::MARK};
|
||||
static const EdgePanelKind kOrder[6] = {EdgePanelKind::COUNT, EdgePanelKind::CENTER,
|
||||
EdgePanelKind::MEAN, EdgePanelKind::PEAK,
|
||||
EdgePanelKind::MARK, EdgePanelKind::RESAMPLE};
|
||||
int openSeen = 0;
|
||||
bool found = false;
|
||||
for (std::size_t lane = 0; lane < m_lanes.size() && !found; ++lane)
|
||||
@@ -701,7 +749,9 @@ namespace jdi
|
||||
const bool scrollable = meanContentHeight > viewHeight + 0.5f;
|
||||
const float maxScroll = std::max(0.0f, meanContentHeight - viewHeight);
|
||||
const float storedScroll = meanFilter ? lane.meanFilterScroll
|
||||
: (weakPeak ? lane.weakPeakScroll : lane.edgeCenterScroll);
|
||||
: (weakPeak ? lane.weakPeakScroll
|
||||
: (resamplePanel ? lane.resampleScroll
|
||||
: lane.edgeCenterScroll));
|
||||
const float scroll = std::max(0.0f, std::min(storedScroll, maxScroll));
|
||||
const float innerWidth =
|
||||
std::max(0.0f, width - pad * 2.0f - (scrollable ? (scrollW + 4.0f * m_scale) : 0.0f));
|
||||
@@ -734,6 +784,14 @@ namespace jdi
|
||||
const float resetGap = kWeakPeakResetGap * m_scale;
|
||||
const float trackHeight = kWeakPeakTrackHeight * m_scale;
|
||||
const float closeSize = kEdgePanelMeanCheckSize * m_scale;
|
||||
// 「重采样」四行的标签列(「最大数据间隔」比均值/弱峰的标签更宽):
|
||||
// 与 `EdgePanelWidth` 用同一组实测宽度,保证字段不会压到标签上。
|
||||
const float resampleLabelColumn =
|
||||
std::max(std::max(UiMeasureTextWidth(m_fonts->uiSmall, kResampleMaxIntervalLabel),
|
||||
UiMeasureTextWidth(m_fonts->uiSmall, kResampleMinIntervalLabel)),
|
||||
std::max(UiMeasureTextWidth(m_fonts->uiSmall, kResampleIntervalLabel),
|
||||
UiMeasureTextWidth(m_fonts->uiSmall, kResampleFrequencyLabel)));
|
||||
const float rowLabelColumn = resamplePanel ? resampleLabelColumn : labelColumn;
|
||||
const float contentLeft = layout.content.X;
|
||||
const float trackLeft = contentLeft + labelColumn + 6.0f * m_scale;
|
||||
const float resetLeft = contentLeft + innerWidth - resetSize;
|
||||
@@ -751,7 +809,13 @@ namespace jdi
|
||||
}
|
||||
bool checked = false;
|
||||
bool itemCollapsed = false;
|
||||
if (weakPeak)
|
||||
if (resamplePanel)
|
||||
{
|
||||
const Lane::ResampleItem* item = lane.FindResampleItem(ref);
|
||||
checked = (item != nullptr);
|
||||
itemCollapsed = (item != nullptr && item->collapsed);
|
||||
}
|
||||
else if (weakPeak)
|
||||
{
|
||||
const Lane::WeakPeakItem* item = lane.FindWeakPeakItem(ref);
|
||||
checked = (item != nullptr);
|
||||
@@ -797,6 +861,42 @@ namespace jdi
|
||||
layout.rowIterTracks.push_back(gd::RectF());
|
||||
layout.rowIterValues.push_back(gd::RectF());
|
||||
layout.rowIterResets.push_back(gd::RectF());
|
||||
if (resamplePanel)
|
||||
{
|
||||
layout.rowInfoMaxInterval.push_back(gd::RectF());
|
||||
layout.rowInfoMinInterval.push_back(gd::RectF());
|
||||
layout.rowIntervalField.push_back(gd::RectF());
|
||||
layout.rowFrequencyField.push_back(gd::RectF());
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (resamplePanel)
|
||||
{
|
||||
// 「重采样」四行:① 最大间隔(只读)② 最小间隔(只读)
|
||||
// ③ 重采样间隔(输入)④ 重采样频率(输入,与③联动)。
|
||||
// 每行 = 左侧标签 + 右侧数值/输入框区域。
|
||||
layout.rowLevelTracks.push_back(gd::RectF());
|
||||
layout.rowLevelValues.push_back(gd::RectF());
|
||||
layout.rowLevelResets.push_back(gd::RectF());
|
||||
layout.rowIterTracks.push_back(gd::RectF());
|
||||
layout.rowIterValues.push_back(gd::RectF());
|
||||
layout.rowIterResets.push_back(gd::RectF());
|
||||
layout.rowInfoMaxInterval.push_back(
|
||||
gd::RectF(contentLeft, rowY, innerWidth, sliderRow));
|
||||
rowY += sliderRow;
|
||||
layout.rowInfoMinInterval.push_back(
|
||||
gd::RectF(contentLeft, rowY, innerWidth, sliderRow));
|
||||
rowY += sliderRow;
|
||||
// 输入行的**数值矩形**靠右对齐,宽度取剩余可用宽度(绘制框与编辑框同源)。
|
||||
const float fieldLeft = contentLeft + rowLabelColumn + 8.0f * m_scale;
|
||||
const float fieldWidth =
|
||||
std::max(24.0f * m_scale, innerWidth - rowLabelColumn - 8.0f * m_scale);
|
||||
layout.rowIntervalField.push_back(
|
||||
gd::RectF(fieldLeft, rowY, fieldWidth, sliderRow));
|
||||
rowY += sliderRow;
|
||||
layout.rowFrequencyField.push_back(
|
||||
gd::RectF(fieldLeft, rowY, fieldWidth, sliderRow));
|
||||
rowY += sliderRow;
|
||||
continue;
|
||||
}
|
||||
// 「中心轴边沿计数」:参数行最上面是**同一行**的「上升沿 / 下降沿」统计。
|
||||
|
||||
Reference in new issue
Block a user