refactor(panels): 删除 rowPoints/rowIterations 死列 + 提取 PaintPanelResampleRows
对应 Code_Review_Report §14.5 第 1 项与第 3 项。
第 1 项:删除 EdgePanelLayout::rowPoints / rowIterations(行数组 18 → 16)
- 写入方只有 AppendRow(推空矩形),读取方只有调试框 AddAll 与 PanelSlotKey 的 mix 循环,
绘制方零(旧式“点数 / 迭代次数”输入框早被滑块版 rowLevelTracks / rowIterTracks 取代)。
- 连带同步:AppendRow、调试框、缓存键、rowgeom 导出、panels/panelscroll 长度不变式、
文档里的列数(18/14 → 16/12)。
等价性由基线证伪,而不是靠人工 grep:
old lines = 983, new lines = 919
only in OLD = 64, only in NEW = 0
only-in-OLD lines NOT belonging to rowPoints/rowIterations = 0
即除被删两列外,其余 919 条键逐字节相同。
第 3 项(前半):提取 PlotView::PaintPanelResampleRows
- PaintPanelListRows 里 `if (resamplePanel) { …; continue; }` 共 132 行整块提取,
签名 (g, layout, rowIndex, laneIndex, ref, item);两个局部 lambda
(paintInfoRow 只读统计行 / paintInputRow 联动输入框)随之迁移。
- maxInterval / minInterval 两个纯拷贝局部量直接用 item.maxInterval / item.minInterval。
- 通用参数行(中心轴统计 + 两行滑块)未提取:需 9 个参数,签名比函数体更难读。
§14.5 第 2 项(完整结构合并)本轮不做,方案已定并写入文档:
- 列视图(PanelColumn<T>)方案否决:拷贝忘重绑 = 静默悬垂,需额外自检兜住;
- 若要做就做“vector<PanelRow> + 具名成员访问(无视图)”,242 处读点(127 处命中判定),
必须单独一轮。它属整洁性收益(漏推一列已被 AppendRow + 长度不变式挡住),不是紧急项。
验证
- make release:零 warning;785,408 → 784,896 B(−512);make debug 同样零 warning
- make -C Tools:零 warning
- UnitTests:checks=260 failed=0 cases=19
- OffscreenChecks -all:7 检查 / 127 断言全 PASS
- panels -mode compare:7 面板像素哈希 + 几何逐项不变(resample=2BFC74B3D9B3239C)
- rowgeom -mode compare:919 + 7 键,0 差异(基线已按 16 列重录)
- PlotViewPaint.cpp 5575 → 5565;PlotViewPanelPaint.cpp 1016 → 1020
文档:Plan §十、Progress 第 38 轮、Report §15、agent_mem 第 38 轮、Tools/README §4.4
This commit is contained in:
1 parent
353c553721
commit
f07ac74988
9 files changed
+384
-150
No files matched your search
@@ -2021,13 +2021,15 @@ namespace jdi
|
|||||||
* @brief 「逐条目」面板的矩形(MEAN / CENTER / PEAK 面板共用同一套排布)。
|
* @brief 「逐条目」面板的矩形(MEAN / CENTER / PEAK 面板共用同一套排布)。
|
||||||
*
|
*
|
||||||
* 索引与 `rowRefs` 一一对应(即网格内可见信号的顺序):
|
* 索引与 `rowRefs` 一一对应(即网格内可见信号的顺序):
|
||||||
* `rowChecks` 为第一排右侧的复选框;`rowPoints` / `rowIterations` 为**输入框**
|
* `rowChecks` 为第一排右侧的复选框;`rowStats` 为第三排的统计结果(中心轴边沿计数专用)。
|
||||||
*(MEAN / CENTER 使用);`rowStats` 为第三排的统计结果(中心轴边沿计数专用)。
|
|
||||||
* 未勾选的条目对应的矩形均为空。
|
* 未勾选的条目对应的矩形均为空。
|
||||||
|
*
|
||||||
|
* ⚠️ 2026-11-04 删除了 `rowPoints` / `rowIterations`:它们是**旧式输入框**(点数 / 迭代次数)
|
||||||
|
* 的矩形,自滑块版排版(`rowLevelTracks` / `rowIterTracks`)上线后就没有绘制方读它们了 ——
|
||||||
|
* 所有写入都是空矩形,读取方只剩调试框与缓存键。保留它们只会让每次 `AppendRow`
|
||||||
|
* 多两次 `push_back`、并且误导后续读者以为“这里还有一套输入框”。
|
||||||
*/
|
*/
|
||||||
std::vector<gd::RectF> rowChecks;
|
std::vector<gd::RectF> rowChecks;
|
||||||
std::vector<gd::RectF> rowPoints;
|
|
||||||
std::vector<gd::RectF> rowIterations;
|
|
||||||
std::vector<gd::RectF> rowStats;
|
std::vector<gd::RectF> rowStats;
|
||||||
/**
|
/**
|
||||||
* @brief 「弱峰调整」每个被勾选信号自己的两行**滑块**(与 `rowRefs` 同序)。
|
* @brief 「弱峰调整」每个被勾选信号自己的两行**滑块**(与 `rowRefs` 同序)。
|
||||||
@@ -2215,7 +2217,7 @@ namespace jdi
|
|||||||
/**
|
/**
|
||||||
* @brief 追加一**行**(条目栏 / 名称 / 三角 / 复选框),其余列一律补空矩形。
|
* @brief 追加一**行**(条目栏 / 名称 / 三角 / 复选框),其余列一律补空矩形。
|
||||||
*
|
*
|
||||||
* ⚠️⚠️ 这是**唯一**允许新增行的入口。面板每行对应 18 个平行数组
|
* ⚠️⚠️ 这是**唯一**允许新增行的入口。面板每行对应 16 个平行数组
|
||||||
* (`rowRefs` / `rowHeaders` / `rowChecks` / `rowLevelTracks` / …);
|
* (`rowRefs` / `rowHeaders` / `rowChecks` / `rowLevelTracks` / …);
|
||||||
* 如果每个排版分支各写一遍 `push_back`,只要某分支漏推一项,
|
* 如果每个排版分支各写一遍 `push_back`,只要某分支漏推一项,
|
||||||
* 那一列之后的所有行都会**读到别人的矩形**("改一次布局就出错"的典型机制)。
|
* 那一列之后的所有行都会**读到别人的矩形**("改一次布局就出错"的典型机制)。
|
||||||
@@ -2236,8 +2238,6 @@ namespace jdi
|
|||||||
rowNameRects.push_back(nameRect);
|
rowNameRects.push_back(nameRect);
|
||||||
rowTriangles.push_back(triangle);
|
rowTriangles.push_back(triangle);
|
||||||
rowChecks.push_back(check);
|
rowChecks.push_back(check);
|
||||||
rowPoints.push_back(gd::RectF());
|
|
||||||
rowIterations.push_back(gd::RectF());
|
|
||||||
rowStats.push_back(gd::RectF());
|
rowStats.push_back(gd::RectF());
|
||||||
rowLevelTracks.push_back(gd::RectF());
|
rowLevelTracks.push_back(gd::RectF());
|
||||||
rowLevelValues.push_back(gd::RectF());
|
rowLevelValues.push_back(gd::RectF());
|
||||||
@@ -2280,6 +2280,26 @@ namespace jdi
|
|||||||
void PaintPanelListRows(gd::Graphics& g, const EdgePanelLayout& layout, const Lane& tabLane,
|
void PaintPanelListRows(gd::Graphics& g, const EdgePanelLayout& layout, const Lane& tabLane,
|
||||||
EdgePanelKind kind, std::size_t laneIndex);
|
EdgePanelKind kind, std::size_t laneIndex);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 「重采样」面板**单个信号的参数区**(四行:最大间隔 / 最小间隔 / 间隔输入 / 频率输入)。
|
||||||
|
*
|
||||||
|
* 从 `PaintPanelListRows` 里提取:这四行的排布与其它面板完全不同(两行只读统计 + 两行
|
||||||
|
* 联动输入框、标签在左数值在右),混在同一个行循环里会让"条目栏 / 参数行 / 重采样行"
|
||||||
|
* 三种排版互相纠缠。提取后调用点只剩一行,`PaintPanelListRows` 只保留
|
||||||
|
* "条目栏 + 通用参数行"两段。
|
||||||
|
* 定义在 `PlotViewPanelPaint.cpp`。
|
||||||
|
*
|
||||||
|
* @param [IN,OUT] g 绘图上下文(调用方已按内容区裁剪)。
|
||||||
|
* @param [IN] layout 面板布局。
|
||||||
|
* @param [IN] rowIndex 行下标(取该行的四个矩形)。
|
||||||
|
* @param [IN] laneIndex 网格下标(判定"正在编辑的是不是这一行")。
|
||||||
|
* @param [IN] ref 该行对应的信号(同上)。
|
||||||
|
* @param [IN] item 该信号的「重采样」条目(已确认存在)。
|
||||||
|
*/
|
||||||
|
void PaintPanelResampleRows(gd::Graphics& g, const EdgePanelLayout& layout, std::size_t rowIndex,
|
||||||
|
std::size_t laneIndex, const SignalRef& ref,
|
||||||
|
const Lane::ResampleItem& item);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 「自由标记」面板的**内容区**(分组栏 + 表头 + 条目 + 吸顶)。
|
* @brief 「自由标记」面板的**内容区**(分组栏 + 表头 + 条目 + 吸顶)。
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -815,8 +815,6 @@ namespace jdi
|
|||||||
boxes.Add(layout.clipDestructiveButton, 3);
|
boxes.Add(layout.clipDestructiveButton, 3);
|
||||||
|
|
||||||
boxes.AddAll(layout.rowChecks, 4);
|
boxes.AddAll(layout.rowChecks, 4);
|
||||||
boxes.AddAll(layout.rowPoints, 4);
|
|
||||||
boxes.AddAll(layout.rowIterations, 4);
|
|
||||||
boxes.AddAll(layout.rowStats, 4);
|
boxes.AddAll(layout.rowStats, 4);
|
||||||
boxes.AddAll(layout.rowLevelTracks, 4);
|
boxes.AddAll(layout.rowLevelTracks, 4);
|
||||||
boxes.AddAll(layout.rowLevelValues, 4);
|
boxes.AddAll(layout.rowLevelValues, 4);
|
||||||
@@ -4088,14 +4086,6 @@ namespace jdi
|
|||||||
{
|
{
|
||||||
mixRect(rect);
|
mixRect(rect);
|
||||||
}
|
}
|
||||||
for (const gd::RectF& rect : layout.rowPoints)
|
|
||||||
{
|
|
||||||
mixRect(rect);
|
|
||||||
}
|
|
||||||
for (const gd::RectF& rect : layout.rowIterations)
|
|
||||||
{
|
|
||||||
mixRect(rect);
|
|
||||||
}
|
|
||||||
for (const gd::RectF& rect : layout.rowStats)
|
for (const gd::RectF& rect : layout.rowStats)
|
||||||
{
|
{
|
||||||
mixRect(rect);
|
mixRect(rect);
|
||||||
|
|||||||
@@ -328,124 +328,10 @@ namespace jdi
|
|||||||
|
|
||||||
// 「重采样」:四行 = 最大间隔(只读)/ 最小间隔(只读)/
|
// 「重采样」:四行 = 最大间隔(只读)/ 最小间隔(只读)/
|
||||||
// 重采样间隔(输入)/ 重采样频率(输入)。标签在左、数值在右。
|
// 重采样间隔(输入)/ 重采样频率(输入)。标签在左、数值在右。
|
||||||
|
// 排布细节见 `PaintPanelResampleRows`(从本循环里提取的独立函数)。
|
||||||
if (resamplePanel)
|
if (resamplePanel)
|
||||||
{
|
{
|
||||||
const float labelWidth =
|
PaintPanelResampleRows(g, layout, rowIndex, index, ref, *resampleItem);
|
||||||
UiMeasureTextWidth(m_fonts->uiSmall, kResampleMaxIntervalLabel);
|
|
||||||
const auto paintInfoRow = [&](const gd::RectF& row, const wchar_t* label,
|
|
||||||
const std::wstring& text)
|
|
||||||
{
|
|
||||||
if (row.Width <= 0.0f)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
UiTextClipped(g, label, m_fonts->uiSmall,
|
|
||||||
gd::RectF(row.X, row.Y, std::max(10.0f, labelWidth),
|
|
||||||
row.Height),
|
|
||||||
m_theme->textMuted, TextAlign::LEFT, layout.content);
|
|
||||||
// ⚠️ 统计数字**绝不省略**(用户 2026-11-03 要求):
|
|
||||||
// 面板宽度已按真实文本量宽(`EdgePanelWidth` 逐信号实测),
|
|
||||||
// 因此这里用 `TextAlign::RIGHT` + 完整矩形即可;
|
|
||||||
// 万一仍被窗口边界夹住,也宁可超出面板右缘(由内容区裁剪兜底),
|
|
||||||
// **不要**退化成 `…` 让用户看不到真实数值。
|
|
||||||
UiTextClipped(g, text, m_fonts->monoSmall,
|
|
||||||
gd::RectF(row.X + labelWidth + 8.0f * m_scale, row.Y,
|
|
||||||
std::max(10.0f, row.Width - labelWidth -
|
|
||||||
8.0f * m_scale),
|
|
||||||
row.Height),
|
|
||||||
m_theme->textStrong, TextAlign::RIGHT,
|
|
||||||
layout.content);
|
|
||||||
};
|
|
||||||
const auto paintInputRow = [&](const gd::RectF& field, const wchar_t* label,
|
|
||||||
const std::wstring& text, bool editing)
|
|
||||||
{
|
|
||||||
if (field.Width <= 0.0f)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// 标签与数值同高:标签从内容区左缘画到字段左缘。
|
|
||||||
UiTextClipped(
|
|
||||||
g, label, m_fonts->uiSmall,
|
|
||||||
gd::RectF(layout.content.X, field.Y,
|
|
||||||
std::max(10.0f, field.X - 8.0f * m_scale - layout.content.X),
|
|
||||||
field.Height),
|
|
||||||
m_theme->textMuted, TextAlign::LEFT, layout.content);
|
|
||||||
if (editing)
|
|
||||||
{
|
|
||||||
return; // 正在编辑:内容由 `PaintEditField` 画
|
|
||||||
}
|
|
||||||
const gd::RectF box(field.X, field.Y + 2.0f * m_scale, field.Width,
|
|
||||||
std::max(10.0f, field.Height - 4.0f * m_scale));
|
|
||||||
UiFillRoundRect(g, box, 3.0f * m_scale, m_theme->pressed);
|
|
||||||
UiDrawRoundRect(g, box, 3.0f * m_scale, m_theme->border, 1.0f);
|
|
||||||
UiTextClipped(g, text, m_fonts->monoSmall,
|
|
||||||
gd::RectF(box.X + 4.0f * m_scale, box.Y,
|
|
||||||
std::max(6.0f, box.Width - 8.0f * m_scale),
|
|
||||||
box.Height),
|
|
||||||
m_theme->textStrong, TextAlign::LEFT, layout.content);
|
|
||||||
};
|
|
||||||
double maxInterval = 0.0;
|
|
||||||
double minInterval = 0.0;
|
|
||||||
const bool hasInterval =
|
|
||||||
(resampleItem->maxInterval > 0.0) && (resampleItem->minInterval > 0.0);
|
|
||||||
maxInterval = resampleItem->maxInterval;
|
|
||||||
minInterval = resampleItem->minInterval;
|
|
||||||
// 「最大 / 最小数据间隔」= 自适应单位的**小数**文本(绝不用科学计数法),
|
|
||||||
// 末尾追加一个括号内的「对应频率」尾缀(单位在 Hz/kHz/MHz/GHz 之间自适应)。
|
|
||||||
//
|
|
||||||
// ⚠️⚠️ 全部使用**真实值**(用户 2026-11-03 明确要求):
|
|
||||||
// * 时间间隔:`FormatDurationCompound` 会把次级单位保留 3 位小数
|
|
||||||
// ⇒ `0.003696631s` 显示为 `3ms 696.631us`(旧实现舍入到 3 位有效数字
|
|
||||||
// 得到 `3ms 700us`,把真实尾数抹掉了);
|
|
||||||
// * 频率:由**未舍入的真实间隔**换算 ⇒ `270.517 Hz`(旧实现用舍入后的
|
|
||||||
// `3.70ms` 换算得到 `270.27 Hz`,与输入框不一致)。
|
|
||||||
// 两条路径都用真实值 ⇒ 统计行与输入框天然一致。
|
|
||||||
const auto intervalWithFrequency = [](double seconds) -> std::wstring
|
|
||||||
{
|
|
||||||
std::wstring text = FormatDurationCompound(seconds);
|
|
||||||
if (std::isfinite(seconds) && seconds > 0.0)
|
|
||||||
{
|
|
||||||
text += L"(";
|
|
||||||
text += FormatFrequencyText(1.0 / seconds);
|
|
||||||
text += L")";
|
|
||||||
}
|
|
||||||
return text;
|
|
||||||
};
|
|
||||||
if (rowIndex < layout.rowInfoMaxInterval.size())
|
|
||||||
{
|
|
||||||
paintInfoRow(layout.rowInfoMaxInterval[rowIndex], kResampleMaxIntervalLabel,
|
|
||||||
hasInterval ? intervalWithFrequency(maxInterval) : L"—");
|
|
||||||
}
|
|
||||||
if (rowIndex < layout.rowInfoMinInterval.size())
|
|
||||||
{
|
|
||||||
paintInfoRow(layout.rowInfoMinInterval[rowIndex], kResampleMinIntervalLabel,
|
|
||||||
hasInterval ? intervalWithFrequency(minInterval) : L"—");
|
|
||||||
}
|
|
||||||
const double interval = (resampleItem->interval > 0.0)
|
|
||||||
? resampleItem->interval
|
|
||||||
: minInterval;
|
|
||||||
const bool editingInterval =
|
|
||||||
(m_editTarget == PlotEditTarget::RESAMPLE_INTERVAL) &&
|
|
||||||
(m_editLane == static_cast<int>(index)) && (m_editMeanRef == ref);
|
|
||||||
const bool editingFrequency =
|
|
||||||
(m_editTarget == PlotEditTarget::RESAMPLE_FREQUENCY) &&
|
|
||||||
(m_editLane == static_cast<int>(index)) && (m_editMeanRef == ref);
|
|
||||||
if (rowIndex < layout.rowIntervalField.size())
|
|
||||||
{
|
|
||||||
// 非编辑态:自适应单位 + 小数(与「最大/最小数据间隔」同一套单位形式)。
|
|
||||||
// 编辑态:只显示**秒**数值(不带单位),由 `PaintEditField` 绘制。
|
|
||||||
paintInputRow(layout.rowIntervalField[rowIndex], kResampleIntervalLabel,
|
|
||||||
FormatIntervalText(interval), editingInterval);
|
|
||||||
}
|
|
||||||
if (rowIndex < layout.rowFrequencyField.size())
|
|
||||||
{
|
|
||||||
// ⚠️ 频率同样用**真实间隔**换算(与时间间隔行同源)⇒ `270.517 Hz`。
|
|
||||||
const double hertz = (interval > 0.0) ? (1.0 / interval) : 0.0;
|
|
||||||
// 非编辑态:频率单位自适应(Hz/kHz/MHz/GHz);编辑态:只显示 Hz 数值。
|
|
||||||
paintInputRow(layout.rowFrequencyField[rowIndex], kResampleFrequencyLabel,
|
|
||||||
(hertz > 0.0) ? FormatFrequencyText(hertz) : L"—",
|
|
||||||
editingFrequency);
|
|
||||||
}
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -669,7 +555,125 @@ namespace jdi
|
|||||||
PaintPanelScrollBar(g, layout, *m_theme);
|
PaintPanelScrollBar(g, layout, *m_theme);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @brief 「重采样」面板**单个信号的参数区**(四行)。
|
||||||
|
*
|
||||||
|
* 从 `PaintPanelListRows` 里整块提取(第 38 轮):这四行的排布(两行只读统计 +
|
||||||
|
* 两行联动输入框、标签在左数值在右)与其它面板的滑块参数行完全不同,
|
||||||
|
* 混在同一个行循环里只会让"条目栏 / 参数行 / 重采样行"三种排版互相纠缠。
|
||||||
|
* **零逻辑改动**,判据 = `rowgeom -mode compare` + `panels -mode compare` 全绿。
|
||||||
|
*/
|
||||||
|
void PlotView::PaintPanelResampleRows(gd::Graphics& g, const EdgePanelLayout& layout,
|
||||||
|
std::size_t rowIndex, std::size_t laneIndex,
|
||||||
|
const SignalRef& ref, const Lane::ResampleItem& item)
|
||||||
|
{
|
||||||
|
const std::size_t index = laneIndex;
|
||||||
|
const float labelWidth =
|
||||||
|
UiMeasureTextWidth(m_fonts->uiSmall, kResampleMaxIntervalLabel);
|
||||||
|
const auto paintInfoRow = [&](const gd::RectF& row, const wchar_t* label,
|
||||||
|
const std::wstring& text)
|
||||||
|
{
|
||||||
|
if (row.Width <= 0.0f)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
UiTextClipped(g, label, m_fonts->uiSmall,
|
||||||
|
gd::RectF(row.X, row.Y, std::max(10.0f, labelWidth), row.Height),
|
||||||
|
m_theme->textMuted, TextAlign::LEFT, layout.content);
|
||||||
|
// ⚠️ 统计数字**绝不省略**(用户 2026-11-03 要求):
|
||||||
|
// 面板宽度已按真实文本量宽(`EdgePanelWidth` 逐信号实测),
|
||||||
|
// 因此这里用 `TextAlign::RIGHT` + 完整矩形即可;
|
||||||
|
// 万一仍被窗口边界夹住,也宁可超出面板右缘(由内容区裁剪兜底),
|
||||||
|
// **不要**退化成 `…` 让用户看不到真实数值。
|
||||||
|
UiTextClipped(g, text, m_fonts->monoSmall,
|
||||||
|
gd::RectF(row.X + labelWidth + 8.0f * m_scale, row.Y,
|
||||||
|
std::max(10.0f, row.Width - labelWidth - 8.0f * m_scale),
|
||||||
|
row.Height),
|
||||||
|
m_theme->textStrong, TextAlign::RIGHT, layout.content);
|
||||||
|
};
|
||||||
|
const auto paintInputRow = [&](const gd::RectF& field, const wchar_t* label,
|
||||||
|
const std::wstring& text, bool editing)
|
||||||
|
{
|
||||||
|
if (field.Width <= 0.0f)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 标签与数值同高:标签从内容区左缘画到字段左缘。
|
||||||
|
UiTextClipped(
|
||||||
|
g, label, m_fonts->uiSmall,
|
||||||
|
gd::RectF(layout.content.X, field.Y,
|
||||||
|
std::max(10.0f, field.X - 8.0f * m_scale - layout.content.X),
|
||||||
|
field.Height),
|
||||||
|
m_theme->textMuted, TextAlign::LEFT, layout.content);
|
||||||
|
if (editing)
|
||||||
|
{
|
||||||
|
return; // 正在编辑:内容由 `PaintEditField` 画
|
||||||
|
}
|
||||||
|
const gd::RectF box(field.X, field.Y + 2.0f * m_scale, field.Width,
|
||||||
|
std::max(10.0f, field.Height - 4.0f * m_scale));
|
||||||
|
UiFillRoundRect(g, box, 3.0f * m_scale, m_theme->pressed);
|
||||||
|
UiDrawRoundRect(g, box, 3.0f * m_scale, m_theme->border, 1.0f);
|
||||||
|
UiTextClipped(g, text, m_fonts->monoSmall,
|
||||||
|
gd::RectF(box.X + 4.0f * m_scale, box.Y,
|
||||||
|
std::max(6.0f, box.Width - 8.0f * m_scale), box.Height),
|
||||||
|
m_theme->textStrong, TextAlign::LEFT, layout.content);
|
||||||
|
};
|
||||||
|
const bool hasInterval = (item.maxInterval > 0.0) && (item.minInterval > 0.0);
|
||||||
|
// 「最大 / 最小数据间隔」= 自适应单位的**小数**文本(绝不用科学计数法),
|
||||||
|
// 末尾追加一个括号内的「对应频率」尾缀(单位在 Hz/kHz/MHz/GHz 之间自适应)。
|
||||||
|
//
|
||||||
|
// ⚠️⚠️ 全部使用**真实值**(用户 2026-11-03 明确要求):
|
||||||
|
// * 时间间隔:`FormatDurationCompound` 会把次级单位保留 3 位小数
|
||||||
|
// ⇒ `0.003696631s` 显示为 `3ms 696.631us`(旧实现舍入到 3 位有效数字
|
||||||
|
// 得到 `3ms 700us`,把真实尾数抹掉了);
|
||||||
|
// * 频率:由**未舍入的真实间隔**换算 ⇒ `270.517 Hz`(旧实现用舍入后的
|
||||||
|
// `3.70ms` 换算得到 `270.27 Hz`,与输入框不一致)。
|
||||||
|
// 两条路径都用真实值 ⇒ 统计行与输入框天然一致。
|
||||||
|
const auto intervalWithFrequency = [](double seconds) -> std::wstring
|
||||||
|
{
|
||||||
|
std::wstring text = FormatDurationCompound(seconds);
|
||||||
|
if (std::isfinite(seconds) && seconds > 0.0)
|
||||||
|
{
|
||||||
|
text += L"(";
|
||||||
|
text += FormatFrequencyText(1.0 / seconds);
|
||||||
|
text += L")";
|
||||||
|
}
|
||||||
|
return text;
|
||||||
|
};
|
||||||
|
if (rowIndex < layout.rowInfoMaxInterval.size())
|
||||||
|
{
|
||||||
|
paintInfoRow(layout.rowInfoMaxInterval[rowIndex], kResampleMaxIntervalLabel,
|
||||||
|
hasInterval ? intervalWithFrequency(item.maxInterval) : L"—");
|
||||||
|
}
|
||||||
|
if (rowIndex < layout.rowInfoMinInterval.size())
|
||||||
|
{
|
||||||
|
paintInfoRow(layout.rowInfoMinInterval[rowIndex], kResampleMinIntervalLabel,
|
||||||
|
hasInterval ? intervalWithFrequency(item.minInterval) : L"—");
|
||||||
|
}
|
||||||
|
const double interval = (item.interval > 0.0) ? item.interval : item.minInterval;
|
||||||
|
const bool editingInterval = (m_editTarget == PlotEditTarget::RESAMPLE_INTERVAL) &&
|
||||||
|
(m_editLane == static_cast<int>(index)) && (m_editMeanRef == ref);
|
||||||
|
const bool editingFrequency = (m_editTarget == PlotEditTarget::RESAMPLE_FREQUENCY) &&
|
||||||
|
(m_editLane == static_cast<int>(index)) && (m_editMeanRef == ref);
|
||||||
|
if (rowIndex < layout.rowIntervalField.size())
|
||||||
|
{
|
||||||
|
// 非编辑态:自适应单位 + 小数(与「最大/最小数据间隔」同一套单位形式)。
|
||||||
|
// 编辑态:只显示**秒**数值(不带单位),由 `PaintEditField` 绘制。
|
||||||
|
paintInputRow(layout.rowIntervalField[rowIndex], kResampleIntervalLabel,
|
||||||
|
FormatIntervalText(interval), editingInterval);
|
||||||
|
}
|
||||||
|
if (rowIndex < layout.rowFrequencyField.size())
|
||||||
|
{
|
||||||
|
// ⚠️ 频率同样用**真实间隔**换算(与时间间隔行同源)⇒ `270.517 Hz`。
|
||||||
|
const double hertz = (interval > 0.0) ? (1.0 / interval) : 0.0;
|
||||||
|
// 非编辑态:频率单位自适应(Hz/kHz/MHz/GHz);编辑态:只显示 Hz 数值。
|
||||||
|
paintInputRow(layout.rowFrequencyField[rowIndex], kResampleFrequencyLabel,
|
||||||
|
(hertz > 0.0) ? FormatFrequencyText(hertz) : L"—", editingFrequency);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
* @brief Marker panel content: per-signal group bars (collapsible) + table header +
|
* @brief Marker panel content: per-signal group bars (collapsible) + table header +
|
||||||
* fixed-point rows, with sticky group bars.
|
* fixed-point rows, with sticky group bars.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -307,3 +307,39 @@
|
|||||||
`SetAllVisible(false)`,而"全部不可见 ⇒ 丢弃网格"是既有设计
|
`SetAllVisible(false)`,而"全部不可见 ⇒ 丢弃网格"是既有设计
|
||||||
(`RebuildLanes` 的 MERGED 分支),固定点/面板开关会随之清空 —— 该场景要**重新播**。
|
(`RebuildLanes` 的 MERGED 分支),固定点/面板开关会随之清空 —— 该场景要**重新播**。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 十、第 38 轮追加(2026-11-04):§14.5 第 1 项(删死列)+ 第 3 项前半(拆重采样行)
|
||||||
|
|
||||||
|
> 触发:用户要求「按照上一轮报告中的 §14.5 下一步(按价值排序)继续推进」。
|
||||||
|
|
||||||
|
| 节点 | 内容 | 验收标准 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| **P31** | 删除 `EdgePanelLayout::rowPoints` / `rowIterations`(写入方只剩 `AppendRow`,读取方只剩调试框与缓存键,几何恒为空) | `rowgeom -mode compare` 的差异**只**来自这两列(64 条键,0 条其它差异);`panels -mode compare` 像素不变 |
|
||||||
|
| **P32** | 从 `PaintPanelListRows` 提取 `PaintPanelResampleRows`("四行只读统计 + 联动输入框"这一段) | 面板像素哈希(尤其 `resample`)与逐行几何**逐项不变** |
|
||||||
|
|
||||||
|
### 10.1 为什么"删掉一列"也可以证明
|
||||||
|
|
||||||
|
`rowgeom` 的两层基线正好给出**可证伪**的等价性:
|
||||||
|
|
||||||
|
* 旧基线 983 行、新基线 919 行;集合差 = 只在旧里的 **64** 行、只在新里的 **0** 行;
|
||||||
|
* 那 64 行**全部**属于 `rowPoints` / `rowIterations`(脚本按正则断言,非人工核对);
|
||||||
|
* ⇒ 其余 919 条键**逐字节相同**,也就是说"删掉的两列从没被赋予过非空矩形"这一判断被基线证实。
|
||||||
|
|
||||||
|
### 10.2 本轮新增硬约束
|
||||||
|
|
||||||
|
1. **`EdgePanelLayout` 行相关的平行数组现在是 16 个**(原 18)。任何"列清单"
|
||||||
|
(`rowgeom` 导出、长度不变式、调试框、缓存键)都要同步;`AppendRow` 里少推一列
|
||||||
|
仍会被长度不变式当场抓到。
|
||||||
|
2. **只读统计/输入框这类"专属排版"要独立成函数**:`PaintPanelResampleRows` 的四行
|
||||||
|
与其它面板的滑块参数行没有共同点,混在同一个行循环里只会让三种排版互相纠缠。
|
||||||
|
3. **§14.5 第 2 项(完整结构合并)仍处于"闸门关闭"状态**,未做(理由见 Report §15.3)。
|
||||||
|
|
||||||
|
### 10.3 §14.5 第 3 项的完成度
|
||||||
|
|
||||||
|
| 段落 | 状态 |
|
||||||
|
| --- | --- |
|
||||||
|
| 重采样行(四行只读+输入) | ✅ 已提取为 `PaintPanelResampleRows` |
|
||||||
|
| 通用参数行(中心轴统计 + 两行滑块) | ⏸ 未提取:依赖 3 个 kind 标志 + 2 套拖拽/编辑状态 + 2 个滑块 lambda,提取需传 9 个参数,可读性收益低于参数表本身 |
|
||||||
|
| 条目栏(三角 + 名称 + 复选框 + 悬停) | 保留在 `PaintPanelListRows` 里(它是三种排版的公共前缀) |
|
||||||
|
|
||||||
@@ -491,3 +491,62 @@ SUMMARY checks=32 failed=1 groups=1 result=FAIL
|
|||||||
| `rowgeom -mode compare -data MF4-Record.MF4` | `detail 1304 键 + scale 7 键`(566 行),0 差异 |
|
| `rowgeom -mode compare -data MF4-Record.MF4` | `detail 1304 键 + scale 7 键`(566 行),0 差异 |
|
||||||
| `panels -mode compare` | 7 个面板像素哈希 + 几何逐项不变 |
|
| `panels -mode compare` | 7 个面板像素哈希 + 几何逐项不变 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 第 38 轮(2026-11-04):删死列 + 拆重采样参数行(§14.5 第 1 / 第 3 项)
|
||||||
|
|
||||||
|
### P31 删除 `rowPoints` / `rowIterations` —— ✅ 完成
|
||||||
|
|
||||||
|
审计(§14.5 第 1 项的前提)在三处确认它们是**死列**:
|
||||||
|
|
||||||
|
| 位置 | 原用途 | 现状 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| 写入 | `AppendRow` 里推两个空矩形 | 这是**唯一**写入点,从未被赋过非空矩形 |
|
||||||
|
| 读取 | `PlotViewPaint.cpp` 调试框 `AddAll`、`PanelSlotKey` 的 mix 循环 | 都是"遍历即忘",不产生任何可见效果 |
|
||||||
|
| 绘制 | 无 | 旧式"点数 / 迭代次数"输入框早被滑块版(`rowLevelTracks` / `rowIterTracks`)取代 |
|
||||||
|
|
||||||
|
改动:删两个 `std::vector<gd::RectF>`(`EdgePanelLayout` 少 48 字节)、`AppendRow` 少两次 `push_back`、
|
||||||
|
调试框与缓存键各少两个循环、`rowgeom` 导出与两处长度不变式各少两项(16 列)。
|
||||||
|
|
||||||
|
**等价性被基线证明**(这是本轮最值得记的一条方法论):
|
||||||
|
|
||||||
|
```text
|
||||||
|
old lines = 983, new lines = 919
|
||||||
|
only in OLD = 64, only in NEW = 0
|
||||||
|
only-in-OLD lines NOT belonging to rowPoints/rowIterations = 0
|
||||||
|
```
|
||||||
|
|
||||||
|
* 983 − 919 = 64,正好是两列在 7 个面板上的键数(`count` 键 + 每行键);
|
||||||
|
* 其余 919 条键**逐字节相同** ⇒ "这两列从未有非空值"的判断不是靠眼睛,而是靠基线证伪。
|
||||||
|
* 随后用新代码重新 `record` 正式基线(`rowgeom-baseline.txt`),再次 `compare` 归零。
|
||||||
|
|
||||||
|
### P32 提取 `PaintPanelResampleRows` —— ✅ 完成
|
||||||
|
|
||||||
|
`PaintPanelListRows` 里 `if (resamplePanel) { …四行… continue; }` 这 **132 行**整块提取为
|
||||||
|
`PlotView::PaintPanelResampleRows(g, layout, rowIndex, laneIndex, ref, item)`:
|
||||||
|
|
||||||
|
* 两个局部 lambda(`paintInfoRow` 只读统计行 / `paintInputRow` 联动输入框)随之迁移;
|
||||||
|
* `maxInterval` / `minInterval` 两个"拷贝一份再说"的局部量直接用 `item.maxInterval` /
|
||||||
|
`item.minInterval`(原来是纯拷贝,无语义差别);
|
||||||
|
* 调用点只剩 3 行(含注释),`PaintPanelListRows` 现在只有"条目栏 + 通用参数行"两段。
|
||||||
|
|
||||||
|
### §14.5 第 3 项的完成度(诚实记录)
|
||||||
|
|
||||||
|
| 段落 | 状态 | 理由 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| 重采样行 | ✅ 已提取 | 排版与其它面板完全不同、入口/出口干净(`continue` 收尾) |
|
||||||
|
| 通用参数行(中心轴统计 + 两行滑块) | ⏸ 未提取 | 依赖 3 个 kind 标志 + 2 套状态(`m_dragMode`/`m_editTarget`)+ 2 个滑块 lambda,签名要约 9 个参数,可读性收益低于参数表成本 |
|
||||||
|
| 条目栏 | 保留 | 三角 + 名称 + 复选框 + 悬停是三种排版的公共前缀 |
|
||||||
|
|
||||||
|
### 本轮验证
|
||||||
|
|
||||||
|
| 项 | 结果 |
|
||||||
|
| --- | --- |
|
||||||
|
| `make release`(全量) | 零 error / 零 warning;产物 785,408 → **784,896 B**(−512) |
|
||||||
|
| `make -C Tools` | 零 error / 零 warning |
|
||||||
|
| `UnitTests` | `checks=260 failed=0 cases=19` |
|
||||||
|
| `OffscreenChecks -all` | 7/7 PASS,`checks=127 failed=0` |
|
||||||
|
| `panels -mode compare` | 7 个面板像素哈希 + 几何**逐项不变**(含 `resample=2BFC74B3D9B3239C`) |
|
||||||
|
| `rowgeom -mode compare` | 919 + 7 键,0 差异 |
|
||||||
|
| 行数 | `PlotViewPaint.cpp` 5575 → 5565;`PlotViewPanelPaint.cpp` 1016 → 1020(净增 4 行:函数头 + 文档) |
|
||||||
|
|
||||||
@@ -610,3 +610,76 @@ Tools/README.md 目录结构、作用、测试项、验收标准、A/B
|
|||||||
否则不建议动。
|
否则不建议动。
|
||||||
3. `PaintPanelListRows` 再拆三段(纯可读性)。
|
3. `PaintPanelListRows` 再拆三段(纯可读性)。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 十五、第 38 轮:§14.5 第 1 / 第 3 项
|
||||||
|
|
||||||
|
### 15.1 结论
|
||||||
|
|
||||||
|
| §14.5 建议 | 结果 |
|
||||||
|
| --- | --- |
|
||||||
|
| 1. `rowPoints` / `rowIterations` 清理 | ✅ 删除(`EdgePanelLayout` 16 列),产物 −512 B,基线证明等价 |
|
||||||
|
| 2. 完整结构合并(`vector<PanelRow>` + 列视图) | ⏸ **未做**,闸门仍关闭(理由见 §15.3) |
|
||||||
|
| 3. `PaintPanelListRows` 再拆 | ◑ 重采样行已提取(132 行),参数行未提取(理由见 §15.4) |
|
||||||
|
|
||||||
|
### 15.2 "删掉一列"的等价性怎么证明
|
||||||
|
|
||||||
|
`rowgeom` 让"删除死列"从"我看过没人用"变成**可验证**:
|
||||||
|
|
||||||
|
```text
|
||||||
|
old lines = 983, new lines = 919
|
||||||
|
only in OLD = 64, only in NEW = 0
|
||||||
|
only-in-OLD lines NOT belonging to rowPoints/rowIterations = 0
|
||||||
|
```
|
||||||
|
|
||||||
|
* 唯一差异就是被删两列的 64 条键;其余 919 条**逐字节相同**;
|
||||||
|
* 因为 `rowgeom` 导出的是**每个矩形的每个字段**,"这两列恒为空"这一前提被直接证伪/证实,
|
||||||
|
不依赖人工 grep 的完整性。
|
||||||
|
|
||||||
|
这给出一条可复用的方法:**先有全字段基线,再删字段**;否则"删掉的东西没人用"永远只是断言。
|
||||||
|
|
||||||
|
### 15.3 为什么 §14.5 第 2 项仍然不做
|
||||||
|
|
||||||
|
报告本人的判据是"若要做,必须同时给 `EdgePanelLayout` 加视图自检,否则不建议动"。
|
||||||
|
本轮把这一步的门槛量化了:
|
||||||
|
|
||||||
|
| 方案 | 改动面 | 失败可见性 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| A:`std::vector<PanelRow>` + 列视图(`PanelColumn<T>`) | 242 处读点(其中 **127** 处在 `PlotViewInteraction.cpp` 的命中判定里)+ 自定义拷贝/赋值 + 视图自检 | 拷贝后忘重绑 = **静默悬垂**,只能靠新增自检兜住 |
|
||||||
|
| B:`std::vector<PanelRow>` + 具名成员访问(无视图) | 同样是 242 处,但所有访问变成 `rows[i].check`,**结构上不可能悬垂** | 编译期全查;无新风险 |
|
||||||
|
|
||||||
|
> 结论:真要做就做 **B**(无视图),A 的"零改动读点"是幻觉 —— 省下的正则改写换来一个
|
||||||
|
> 必须靠新自检才能发现的隐患。B 的代价是 242 处机械替换(含 30 余处 range-for 与 `.size()`),
|
||||||
|
> 适合单独一轮、以 `rowgeom` + `panels` 双基线为判据,而不是塞在本轮末尾。
|
||||||
|
|
||||||
|
另外现在"漏推一列"已被 `AppendRow` + 长度不变式双重挡住,
|
||||||
|
B 的**剩余收益**主要是"单一分配 + 少 15 个 vector 头"(`EdgePanelLayout` 每实例省约 360 B
|
||||||
|
头部,566 个网格时约 200 KB,但那是**布局对象**、每帧临时构造,实际峰值影响很小)。
|
||||||
|
因此 B 的优先级应低于"面板结构再简化"的实际痛点,属于**整洁性收益**而非正确性收益。
|
||||||
|
|
||||||
|
### 15.4 §14.5 第 3 项的取舍
|
||||||
|
|
||||||
|
| 段落 | 状态 |
|
||||||
|
| --- | --- |
|
||||||
|
| 重采样行(132 行:两行只读统计 + 两行联动输入框) | ✅ 提取为 `PaintPanelResampleRows`,零逻辑改动 |
|
||||||
|
| 通用参数行(中心轴统计 + 两行滑块) | ⏸ 提取需 9 个参数(`layout`/`rowIndex`/`laneIndex`/`ref`/`kind`/`resolved`/`item`/`peakItem`/`tabLane`),签名比函数体更难读 |
|
||||||
|
| 条目栏 | 保留(三种排版的公共前缀) |
|
||||||
|
|
||||||
|
### 15.5 验证
|
||||||
|
|
||||||
|
| 项 | 结果 |
|
||||||
|
| --- | --- |
|
||||||
|
| `make release` | 零 warning;**784,896 B**(−512) |
|
||||||
|
| `UnitTests` | 260 / 19 PASS |
|
||||||
|
| `OffscreenChecks -all` | 7 / 127 PASS |
|
||||||
|
| `panels -mode compare` | 7 面板像素哈希逐项不变 |
|
||||||
|
| `rowgeom -mode compare` | 919 + 7 键,0 差异 |
|
||||||
|
|
||||||
|
### 15.6 下一步(更新)
|
||||||
|
|
||||||
|
1. **方案 B(`vector<PanelRow>` 无视图)单独一轮**:判据 = 编译通过 + `rowgeom`/`panels` 双基线归零;
|
||||||
|
预期收益是"字段重载(`rowRefs` 双语义)"这类问题从**结构上消失** —— 这比省字节更重要。
|
||||||
|
2. 若要在 B 之上再做**语义化**:把 `rowRefs` 的双语义拆成 `rows[].ref` + `markerGroups[]`,
|
||||||
|
让"MARK 面板 12 列为空"的注释消失。
|
||||||
|
3. 面板几何算式重写(§13.6 遗留):基线已就绪,但需先完成 1 或 2。
|
||||||
|
|
||||||
@@ -10638,3 +10638,60 @@ MARK 面板**只填** `rowNameRects` / `rowNameCloses`,其余 14 列**必须
|
|||||||
`EdgePanelLayout` 加"视图自检"(比对 `rows` 容量与视图长度)。
|
`EdgePanelLayout` 加"视图自检"(比对 `rows` 容量与视图长度)。
|
||||||
3. `PaintPanelListRows` 再拆三段(纯可读性)。
|
3. `PaintPanelListRows` 再拆三段(纯可读性)。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2026-11-04 · 第 38 轮(`Code_Review` 分支):删死列 + 拆重采样参数行(§14.5 第 1 / 第 3 项)
|
||||||
|
|
||||||
|
> 规则原文:`Documents/Code_Review_Report.md` §15、`Code_Review_Plan.md` §十、
|
||||||
|
> `Code_Review_Progress.md` 第 38 轮。
|
||||||
|
|
||||||
|
### 1. ✅ 删除 `EdgePanelLayout::rowPoints` / `rowIterations`(行数组 18 → **16**)
|
||||||
|
|
||||||
|
* 写入方**只有** `AppendRow`(推空矩形),读取方**只有**调试框 `AddAll` 与 `PanelSlotKey` 的 mix 循环,
|
||||||
|
绘制方零(旧式"点数 / 迭代次数"输入框早被滑块版取代)。
|
||||||
|
* 任何"面板行数组清单"现在都是 **16 列**:`rowgeom` 导出、`panels`/`panelscroll` 长度不变式、
|
||||||
|
调试框、缓存键、`AppendRow`。
|
||||||
|
|
||||||
|
### 2. ⭐ 方法论:**先有全字段基线,再删字段**
|
||||||
|
|
||||||
|
本轮"删两列"的等价性不是靠 grep 完整性,而是靠 `rowgeom` 证伪:
|
||||||
|
|
||||||
|
```text
|
||||||
|
old lines = 983, new lines = 919 ; only in OLD = 64 ; only in NEW = 0
|
||||||
|
only-in-OLD lines NOT belonging to rowPoints/rowIterations = 0
|
||||||
|
```
|
||||||
|
|
||||||
|
⇒ 以后删任何 `EdgePanelLayout` 字段,都走这个流程:
|
||||||
|
`record 旧 → 改代码 → record 新到独立目录 → 集合差只应包含被删字段 → 再 record 正式基线`。
|
||||||
|
|
||||||
|
### 3. ✅ 提取 `PaintPanelResampleRows`(132 行)
|
||||||
|
|
||||||
|
`PaintPanelListRows` 里 `if (resamplePanel) { …; continue; }` 整块提取;签名
|
||||||
|
`(g, layout, rowIndex, laneIndex, ref, item)`;两个局部 lambda(只读统计行 / 联动输入框)随之迁移。
|
||||||
|
**通用参数行(中心轴统计 + 两行滑块)未提取**:需 9 个参数,签名比函数体更难读。
|
||||||
|
|
||||||
|
### 4. ⛔ §14.5 第 2 项(完整结构合并)**仍未做**,且方案已定
|
||||||
|
|
||||||
|
| 方案 | 结论 |
|
||||||
|
| --- | --- |
|
||||||
|
| A:`vector<PanelRow>` + `PanelColumn<T>` 列视图 | ❌ 不做:242 处读点看似"零改动",但自定义拷贝忘重绑 = **静默悬垂**,只能靠新增自检兜住 |
|
||||||
|
| B:`vector<PanelRow>` + 具名成员访问(无视图) | ✅ 若要做就做这个:所有访问变 `rows[i].check`,**结构上不可能悬垂**,可省略自检 |
|
||||||
|
|
||||||
|
B 的规模:242 处读点(**127** 在 `PlotViewInteraction.cpp` 命中判定里)+ 30 余处 range-for / `.size()`。
|
||||||
|
**必须单独一轮**,判据 = 编译通过 + `rowgeom`/`panels` 双基线归零。
|
||||||
|
B 属**整洁性收益**(漏推一列已被 `AppendRow` + 长度不变式挡住),不是正确性收益 —— 别再当紧急项。
|
||||||
|
|
||||||
|
### 5. 本轮数字
|
||||||
|
|
||||||
|
* Release 785,408 → **784,896 B**(−512);零 warning。
|
||||||
|
* `UnitTests` 260/19;`OffscreenChecks -all` 7 检查 / 127 断言全 PASS。
|
||||||
|
* `panels -mode compare` 7 面板像素哈希逐项不变(含 `resample=2BFC74B3D9B3239C`)。
|
||||||
|
* `rowgeom -mode compare` 919 + 7 键 0 差异(基线已按 16 列重新录)。
|
||||||
|
* `PlotViewPaint.cpp` 5575 → 5565;`PlotViewPanelPaint.cpp` 1016 → 1020。
|
||||||
|
|
||||||
|
### 6. 下一步
|
||||||
|
|
||||||
|
1. 方案 B(`vector<PanelRow>` 无视图)单独一轮;顺带把 `rowRefs` 的双语义拆成
|
||||||
|
`rows[].ref` + `markerGroups[]`,让"MARK 面板 12 列为空"的注释消失。
|
||||||
|
2. 面板几何算式重写(§13.6 遗留):基线已就绪,但需先完成 1。
|
||||||
|
|
||||||
@@ -345,7 +345,7 @@ namespace jdi
|
|||||||
// ---- 每一行 / 每一列 ----
|
// ---- 每一行 / 每一列 ----
|
||||||
// ⚠️ **每一列只按自己的长度导出**,不要假设所有列等长:
|
// ⚠️ **每一列只按自己的长度导出**,不要假设所有列等长:
|
||||||
// 「自由标记」面板复用 `rowRefs` 表示"信号分组",只填
|
// 「自由标记」面板复用 `rowRefs` 表示"信号分组",只填
|
||||||
// `rowNameRects` / `rowNameCloses`,其余 14 列**全为空**
|
// `rowNameRects` / `rowNameCloses`,其余 12 列**全为空**
|
||||||
// (这一点正是 `rowgeom` 第一次运行就抓出来的结构事实)。
|
// (这一点正是 `rowgeom` 第一次运行就抓出来的结构事实)。
|
||||||
// 按 `rowRefs.size()` 统一索引会越界崩溃。
|
// 按 `rowRefs.size()` 统一索引会越界崩溃。
|
||||||
const auto dumpRects = [&text, &key](const char* name, const std::vector<gd::RectF>& rects) {
|
const auto dumpRects = [&text, &key](const char* name, const std::vector<gd::RectF>& rects) {
|
||||||
@@ -371,8 +371,6 @@ namespace jdi
|
|||||||
dumpRects("rowNameCloses", layout.rowNameCloses);
|
dumpRects("rowNameCloses", layout.rowNameCloses);
|
||||||
dumpRects("rowTriangles", layout.rowTriangles);
|
dumpRects("rowTriangles", layout.rowTriangles);
|
||||||
dumpRects("rowChecks", layout.rowChecks);
|
dumpRects("rowChecks", layout.rowChecks);
|
||||||
dumpRects("rowPoints", layout.rowPoints);
|
|
||||||
dumpRects("rowIterations", layout.rowIterations);
|
|
||||||
dumpRects("rowStats", layout.rowStats);
|
dumpRects("rowStats", layout.rowStats);
|
||||||
dumpRects("rowLevelTracks", layout.rowLevelTracks);
|
dumpRects("rowLevelTracks", layout.rowLevelTracks);
|
||||||
dumpRects("rowLevelValues", layout.rowLevelValues);
|
dumpRects("rowLevelValues", layout.rowLevelValues);
|
||||||
@@ -667,9 +665,9 @@ namespace jdi
|
|||||||
|
|
||||||
// 不变式:行的几何数组必须与行数**形状一致**。
|
// 不变式:行的几何数组必须与行数**形状一致**。
|
||||||
//
|
//
|
||||||
// ⚠️⚠️ 判据必须**按排版类别**区分 —— 旧版"18 个数组全部全等"是**错的**:
|
// ⚠️⚠️ 判据必须**按排版类别**区分 —— 旧版"所有数组全部全等"是**错的**:
|
||||||
// 「自由标记」面板复用 `rowRefs` 表示"信号分组",只填
|
// 「自由标记」面板复用 `rowRefs` 表示"信号分组",只填
|
||||||
// `rowNameRects` / `rowNameCloses`,其余 14 列**必须为空**
|
// `rowNameRects` / `rowNameCloses`,其余 12 列**必须为空**
|
||||||
// (它真正的行在 `markerRows` / `markerGroups` 里)。
|
// (它真正的行在 `markerRows` / `markerGroups` 里)。
|
||||||
// 这是 `rowgeom` 第一次带真实固定点运行时越界崩溃所暴露的结构事实。
|
// 这是 `rowgeom` 第一次带真实固定点运行时越界崩溃所暴露的结构事实。
|
||||||
const PanelStyle rowStyle = PanelStyleOf(kind);
|
const PanelStyle rowStyle = PanelStyleOf(kind);
|
||||||
@@ -685,13 +683,12 @@ namespace jdi
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
const std::size_t parallelSizes[] = {
|
const std::size_t parallelSizes[] = {
|
||||||
layout.rowHeaders.size(), layout.rowNameRects.size(),
|
layout.rowHeaders.size(), layout.rowNameRects.size(),
|
||||||
layout.rowTriangles.size(), layout.rowChecks.size(),
|
layout.rowTriangles.size(), layout.rowChecks.size(),
|
||||||
layout.rowPoints.size(), layout.rowIterations.size(),
|
layout.rowStats.size(), layout.rowLevelTracks.size(),
|
||||||
layout.rowStats.size(), layout.rowLevelTracks.size(),
|
layout.rowLevelValues.size(), layout.rowLevelResets.size(),
|
||||||
layout.rowLevelValues.size(), layout.rowLevelResets.size(),
|
layout.rowIterTracks.size(), layout.rowIterValues.size(),
|
||||||
layout.rowIterTracks.size(), layout.rowIterValues.size(),
|
layout.rowIterResets.size(), layout.rowInfoMaxInterval.size(),
|
||||||
layout.rowIterResets.size(), layout.rowInfoMaxInterval.size(),
|
|
||||||
layout.rowInfoMinInterval.size(), layout.rowIntervalField.size(),
|
layout.rowInfoMinInterval.size(), layout.rowIntervalField.size(),
|
||||||
layout.rowFrequencyField.size()};
|
layout.rowFrequencyField.size()};
|
||||||
for (const std::size_t size : parallelSizes)
|
for (const std::size_t size : parallelSizes)
|
||||||
@@ -868,8 +865,6 @@ namespace jdi
|
|||||||
initial.rowNameRects.size(),
|
initial.rowNameRects.size(),
|
||||||
initial.rowTriangles.size(),
|
initial.rowTriangles.size(),
|
||||||
initial.rowChecks.size(),
|
initial.rowChecks.size(),
|
||||||
initial.rowPoints.size(),
|
|
||||||
initial.rowIterations.size(),
|
|
||||||
initial.rowStats.size(),
|
initial.rowStats.size(),
|
||||||
initial.rowLevelTracks.size(),
|
initial.rowLevelTracks.size(),
|
||||||
initial.rowLevelValues.size(),
|
initial.rowLevelValues.size(),
|
||||||
@@ -953,7 +948,7 @@ namespace jdi
|
|||||||
*
|
*
|
||||||
* 为什么需要它(Code Review 报告 §13.6 建议 1):
|
* 为什么需要它(Code Review 报告 §13.6 建议 1):
|
||||||
* * 像素哈希只能回答"有没有变化",回答不了"**哪一行哪一列**变了";
|
* * 像素哈希只能回答"有没有变化",回答不了"**哪一行哪一列**变了";
|
||||||
* * `EdgePanelLayout` 仍是 18 个平行数组,任何"合并成单一 `PanelRow`"或
|
* * `EdgePanelLayout` 仍是 16 个平行数组,任何"合并成单一 `PanelRow`"或
|
||||||
* "重写布局算式"的重构都必须先能**逐项证明等价**;
|
* "重写布局算式"的重构都必须先能**逐项证明等价**;
|
||||||
* * 面板改布局时的真实缺陷形态(某列读到别人的矩形)会让**成片的**行错位,
|
* * 面板改布局时的真实缺陷形态(某列读到别人的矩形)会让**成片的**行错位,
|
||||||
* 而像素基线在面板互相重叠时还会被"下层露出"掩盖。
|
* 而像素基线在面板互相重叠时还会被"下层露出"掩盖。
|
||||||
|
|||||||
+1
-1
@@ -214,7 +214,7 @@ OffscreenChecks.exe -run panels -mode compare -out ..\Temp\tools\panel-before
|
|||||||
|
|
||||||
* 列表式面板(均值/中心轴/弱峰/重采样/截取):`rowRefs[i]` = 第 i 行的信号;
|
* 列表式面板(均值/中心轴/弱峰/重采样/截取):`rowRefs[i]` = 第 i 行的信号;
|
||||||
* 「自由标记」面板:`rowRefs[i]` = 第 i 个**信号分组**,真正的条目行在 `markerRows`,
|
* 「自由标记」面板:`rowRefs[i]` = 第 i 个**信号分组**,真正的条目行在 `markerRows`,
|
||||||
且只填 `rowNameRects` / `rowNameCloses`,其余 14 列**必须为空**。
|
且只填 `rowNameRects` / `rowNameCloses`,其余 12 列**必须为空**。
|
||||||
|
|
||||||
所以判据按 `PanelStyleOf(kind).markerGroups` 分流(见 `Checks.cpp` 里 `panels` 的注释)。
|
所以判据按 `PanelStyleOf(kind).markerGroups` 分流(见 `Checks.cpp` 里 `panels` 的注释)。
|
||||||
早先“18 列全等”的写法对 MARK 面板是**错的**,只因既有场景恰好未触发才没暴露。
|
早先“18 列全等”的写法对 MARK 面板是**错的**,只因既有场景恰好未触发才没暴露。
|
||||||
|
|||||||
Reference in new issue
Block a user