.mat 改为流式解码(内存映射 + 逐 chunk 填充);MF4 支持 ##DL/##DZ/##HL;光标数据点被时间面板遮住时改三角

1) .mat(BIG_MAT.mat 1.29GB / 5 信号 x 16,142,471 采样)
   旧链路同时驻留三份内存:整文件 fread 进 vector、整个数据集解压成字节数组、再 memcpy 成
   double;外加 streams 缓存与每条信号各一份时间列拷贝。实测私有内存 +1233.9MB、117s。
   现改为:
   - MatHdf5::Open 用 CreateFileW + CreateFileMappingW + MapViewOfFile(MappedBytes 只读视图,
     解析代码零改动;虚拟地址不等于物理内存,未访问页不驻留);
   - 新增 ReadDatasetInfo(只读元信息)+ StreamDataset(ByteSink)(连续布局 4MB 切片、
     分块布局逐 chunk inflate)+ ReadDoubleStream(SDI #data# 字节流按 8 字节一组直接写 double,
     7 字节尾巴处理非 8 对齐 chunk);ReadNumbers 改为流式转换;
   - MatLoader 先收集信号清单,再按(分组,时间流)合并为一个 TableBlock:时间列按内容去重、
     数据列直接 move;进度改成 0-0.02 映射 / 0.02-0.98 逐流解码 / 1.0。
   结果:117.3s -> 12.5s,私有内存 +1233.9MB -> +740.4MB(最终列数据 738.9MB)。
   关键性能修正:SDI 的 #data# 是 dims=[N]、chunk=[N,1],归一化后每行只有 1 字节;旧的逐行回调
   (且每行分配 coordinate 向量)只有 11MB/s。把目标连续的行合并成一次回调后约 110MB/s。
   顺带把 inflate 的 Huffman 解码改成 9 位快查表,并抽到新的 Deflate.hpp/.cpp 供 MF4 复用。

2) MDF4 数据块
   Problem-MF4.mf4 的第二个数据组把记录写在 ##DL(数据列表,两个 ##DT:131040+504 字节)
   里,旧实现只认 ##DT,整组被跳过,于是 8 个信号只读出 4 个。
   新增 CollectDataSegments(递归展开 ##DL / ##HL,叶子识别 ##DT / ##DV / ##DZ)与
   ReadRecordSegments(跨段拼接、压缩段先 inflate、非压缩段仍按 4MB 分块读),
   压缩段的记录长度改用 max(cg_record_id_len*8, 各通道 bitOffset+bitCount) 推算(载荷是压缩长度)。
   实测 8 个信号:gVP_WindowLearnSt[0] 94 行、gVR_WinOpenReq[0] 9396 行,时间列单调。
   ##SD(已排序数据)仍未实现,这类数据组会被跳过。

   另按要求只做检查、不改代码:该文件中 gVP_WindowLearnSt[0] 与 gVR_WinOpenReq[0] 都存在
   值枚举文本(##CC,cc_type=7;links[4+i] 对应 cc_val[i]),清单已写入 Documents/agent_mem.md。

3) 光标数据点标记
   光标的时间读数与差值胶囊绘制在绘图区底部、且在取值标注之后绘制,靠近底部的数据点圆点
   会被完全盖住。新增 MarkerCoveredByCursorLabel(圆心落入面板或重叠面积达到半个圆)
   与 CoverHiddenMarker(改成向下的三角、锚点放到面板上沿上方),在原始信号与派生曲线胶囊
   两处调用,语义与“数据点在视界下方”一致。

验证(全部离屏:SW_HIDE 窗口 + 只注入合成消息 + BELOW_NORMAL,不占用鼠标键盘屏幕):
- 新增 .preflight/round4check 34/34;A/B:临时禁用 ##DL 支持 => 2 项如实失败;
  临时禁用 CoverHiddenMarker => 3 项如实失败(含像素断言为 0);
- 回归:.preflight/round3check 31/31、editcheck 20/20、check 137/137;
- make release / make debug 零警告;Build/Release/Target.exe 785,920 字节(< 800KB)。
This commit is contained in:
DeepSeek V4.1 Flash committed 2026-10-19 20:13:17 +08:00
1 parent be76f523f3
commit 75f0fafd41
10 files changed
+1677 -795

No files matched your search

+27
View File
@@ -1984,6 +1984,33 @@ namespace jdi
void PlaceCursorLabel(gd::RectF& rect, float preferredTop, float textHeight, float clipTop,
float clipBottom);
/**
* @brief 判断数据点圆点是否被「光标时间读数 / Δt 面板」盖住。
*
* 光标的时间读数与 Δt 胶囊绘制在绘图区底部、且在取值标注**之后**绘制,
* 因此靠近底部的数据点圆点会被它们完全遮住(用户 2026-10-19 报告“看不清数据点在哪”)。
* 判定为“盖住”时调用方应把圆点改成**向下的三角**(与数据点在视界下方同一语义)。
*
* @param [IN] marker 数据点标记。
* @param [IN] radius 圆点半径。
* @param [OUT] hit 遮住它的面板矩形(用于把三角贴在面板上沿)。
* @return 被盖住返回 true。
*/
bool MarkerCoveredByCursorLabel(const DataMarker& marker, float radius, gd::RectF& hit) const;
/**
* @brief 若数据点圆点被底部时间读数/Δt 面板盖住,把它改成**向下的三角**。
*
* 语义与“数据点位于视界下方”一致(用户 2026-10-19 要求):圆点既然看不见,
* 就用贴面板上沿的向下三角告知“这里有个数据点、它在下侧”。
* 已经是越界三角的标记不做任何修改。
*
* @param [IN,OUT] marker 标记几何。
* @param [IN] radius 圆点半径。
* @param [IN] clipTop 网格可见区上边界(三角不会越过它)。
*/
void CoverHiddenMarker(DataMarker& marker, float radius, float clipTop) const;
/**
* @brief 统计某信号穿越横向光标幅值的上/下边沿数量,结果带缓存。
*
@@ -2802,6 +2802,51 @@ namespace jdi
return marker;
}
bool PlotView::MarkerCoveredByCursorLabel(const DataMarker& marker, float radius,
gd::RectF& hit) const
{
const gd::RectF circle(marker.x - radius, marker.y - radius, radius * 2.0f, radius * 2.0f);
for (const gd::RectF& rect : m_cursorLabelRects)
{
if (rect.Width <= 0.0f || rect.Height <= 0.0f)
{
continue;
}
// 圆心落在面板内 ⇒ 肯定被遮住(最常见:圆点正好压在时间读数上)。
if (UiPointInRect(rect, marker.x, marker.y))
{
hit = rect;
return true;
}
// 否则看重叠面积:超过圆点外接矩形的一半也算“基本看不见”。
gd::RectF overlap = circle;
overlap.Intersect(rect);
if (overlap.Width > 0.0f && overlap.Height > 0.0f &&
overlap.Width * overlap.Height >= circle.Width * circle.Height * 0.5f)
{
hit = rect;
return true;
}
}
return false;
}
void PlotView::CoverHiddenMarker(DataMarker& marker, float radius, float clipTop) const
{
if (marker.offUp || marker.offDown || marker.offLeft || marker.offRight)
{
return; // 已经是越界三角:保持原语义
}
gd::RectF covering;
if (!MarkerCoveredByCursorLabel(marker, radius, covering))
{
return;
}
marker.offDown = true;
marker.y = std::max(clipTop + kMarkerEdgeInset * m_scale,
covering.Y - (kMarkerTriangleDepth * 0.5f + 1.0f * m_scale));
}
void PlotView::PlaceCursorLabel(gd::RectF& rect, float preferredTop, float textHeight,
float clipTop, float clipBottom)
{
@@ -4989,6 +4989,10 @@ namespace jdi
{
marker.y = labelRect.Y + labelRect.Height * 0.5f;
}
// ⚠️ 数据点圆点被底部的「时间读数 / Δt 面板」盖住时,圆点根本看不见
//(时间读数在取值标注之后绘制、直接盖在上面)。此时改成**向下的三角**。
CoverHiddenMarker(marker, radius, clipTop);
PaintDataMarker(g, marker, radius, signalColor, m_theme->cardBg);
const UiInk ink = UiContrastInk(signalColor);
@@ -5113,6 +5117,8 @@ namespace jdi
{
centerMarker.y = centerRect.Y + centerRect.Height * 0.5f;
}
// 同原始信号:圆点被底部时间读数面板盖住时改为向下三角。
CoverHiddenMarker(centerMarker, radius, clipTop);
PaintDataMarker(g, centerMarker, radius, centerColor, m_theme->cardBg);
// 颜色样式与原始信号一致(同底色/同描边/同文字配色)。
const UiInk centerInk = UiContrastInk(centerColor);