Files
Just-DataInspector/Utilities/Inc/MatH5.hpp
T
DeepSeek-V4.1-Flash 5b24dacee0 悬停高亮恢复旧版外延观感 + 支持 MATLAB .mat(v7.3 / HDF5,嵌套分组)
一、悬停高亮「几乎看不到、只是曲线换了颜色」(对比提交 f845616 的旧版观感)
根因:调色板第 1 色就是主题强调色(0xFF268BD2),而上一轮为提速把稠密信号的光晕改成
「按包络带原始几何 FillPath」——与曲线**逐像素同形**,于是曲线上的那层半透明强调色
完全看不出变化;旧版观感来自「lineWidth+4 的描边笔向两侧各外扩 2~3px」。
修法(PlotViewPaint.cpp::PaintSignalHaloFor):
1) 逐列成对填四边形,上下左右都外扩 grow = (lineWidth + 4) * scale / 2
   (竖向跃变处旧版 5px 笔涂出的正是那条整高色带);
2) 不能把整条包络拼成一个多边形:满幅方波相邻列区间互相交叉,缠绕填充规则会在交叠处
   相互抵消 ⇒ 填充变成一条条竖缝(实测只覆盖约一半带宽);
3) alpha 从 0x55 调回 0x66(与旧版描边笔一致)。
代价仍只在「换信号/换视界/改样式」时付一次,此后每帧只 AlphaBlend。
三维对照(新增 haloshot 探针 + f845616 工作树构建):变化像素 旧 73726 / 上一轮 13529 /
本轮 74916,目视与旧版一致(跃变处的整高色带恢复)。

二、MATLAB .mat(v7.3 = HDF5)支持,含嵌套分组
新增只读 HDF5 子集(不引第三方库):Utilities/Inc/MatH5.hpp + Utilities/Src/MatH5.cpp
- 超级块 v0/v2;对象头 v1(含续块,主块长度以 headerSize 为准);符号表(B-tree v1 +
  本地堆)与 link 消息;数据空间 v1/v2;数据类型 v1;数据布局 v3(紧凑/连续/分块);
  分块数据集支持 deflate(zlib);地址一律相对基地址解析。
新增 MATLAB 语义层:Utilities/Inc/MatLoader.hpp + Utilities/Src/MatLoader.cpp
1) Simulink SDI / SimulationData.Dataset(识别 #refs# + #sigstream#):
   · #refs# 元素有 ElementType(signal/dataset)、Name、Values;
   · timeseries 的 Values/DataR2、Values/TimeR2 是 #sigstream#/<n> 的流编号;
     #data# 是 uint8 字节流,按 8 字节一组解释为 double(与 #length#/#sampleSize# 一致);
   · 子数据集的 Values 是对象引用数组(子元素)⇒ 还原嵌套树;
   · 分组名 = 从根到信号的各级数据集名用 - 连接(如 A/A1/XYZ → 组 A-A1、信号 XYZ),
     信号名取元素 Name(空则退回 timeseries 的 Name,再退回 信号N)。
2) 普通 v7.3 变量:顶层数值矩阵按「列 = 信号、行 = 采样」读取(时间轴 = 采样序号),
   结构体按字段递归。
加载管线扩成多分组:新增 LoadSignalSourceGroups()(DataLoader)→
AsyncLoadState::groups → Application::LoadFile 逐个 AddGroup;其它格式仍只有一个分组。
文件对话框与 IsSupportedSignalFile 增加 .mat。

三、验证(全部离屏,不占用鼠标/键盘/屏幕)
- 新增 haloshot(截图对照 f845616)、matcheck(15/15:4 组 8 信号、时间轴单调、采样数正确)、
  matui(11/11:Application::LoadFile 端到端把 4 个分组加进数据层与信号列表);
- 回归 verify 49/49、panelanim 158/158、gridanim 93/93、hoverdrag 20/20、lanescroll 23/23、
  datacheck 69/69、edgecheck 46/46、errcheck 19/19、halocheck 16/16、panflash 19/19、
  panelstutter 8/8、stress 6/6;
- make release / make debug 零警告;Makefile 与探针构建脚本补 -lz。

文档:Documents/agent_mem.md 追加本轮小节(含 4 条新规则:叠加层必须自带外扩、
缠绕填充不可用于自交包络带、HDF5 主消息块长度看 headerSize、
MATLAB 分块维度可能比数据空间多一维、按右侧补 1 对齐)。
2026-10-19 00:52:41 +08:00

146 lines
5.3 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#ifndef __MAT_H5_HPP__
#define __MAT_H5_HPP__
#include <cstdint>
#include <string>
#include <utility>
#include <vector>
namespace jdi
{
/**
* @brief 只读的 HDF5 子集解析器(用于 MATLAB 7.3 的 `.mat` 文件)。
*
* MATLAB 7.3(`-v7.3`)的 `.mat` 其实是 **HDF5** 文件:数值矩阵是数据集,
* 结构体是分组(字段 = 成员),元信息放在属性里(如 `MATLAB_class`)。
* 本项目不引入 HDF5 库,因此这里实现一个**刚好够用**的只读子集:
*
* * 超级块 v0/v1/v2/v3;
* * 对象头 v1(含续块消息),v2 仅识别并跳过;
* * 分组成员:符号表(B-tree v1 + 本地堆)与 link 消息两种方式;
* * 数据空间 v1/v2、数据类型 v1(整数 / 浮点 / 定长字符串 / 引用 / 复合 / vlen);
* * 数据布局 v3(紧凑 / 连续 / 分块),分块数据集支持 deflate 压缩(zlib)。
*
* ⚠️ 不支持:外部数据文件、共享消息表、非 deflate 过滤器、极老的 v1 布局。
* 这些在 MATLAB 保存的文件里都不出现。
*/
class MatHdf5
{
public:
/** @brief 数据类型类别(对应 HDF5 的 class 编号)。 */
enum class TypeClass : int
{
Integer = 0,
Float = 1,
Time = 2,
String = 3,
Bitfield = 4,
Opaque = 5,
Compound = 6,
Reference = 7,
Enum = 8,
VariableLength = 9,
Array = 10
};
/** @brief 数据类型(只保留读取 MATLAB 数据所需的字段)。 */
struct Type
{
TypeClass cls = TypeClass::Opaque;
int version = 1;
std::size_t size = 0; /**< 单个元素占用的字节数。 */
bool isSigned = false; /**< 整数是否有符号。 */
bool bigEndian = false; /**< 字节序(MATLAB 一律小端)。 */
bool isVariableString = false; /**< vlen 字符串。 */
std::vector<std::pair<std::string, Type>> members; /**< 复合类型成员。 */
};
/** @brief 数据布局。 */
enum class LayoutClass
{
Compact = 0,
Contiguous = 1,
Chunked = 2
};
/** @brief 一个数据集的定义与数据。 */
struct Dataset
{
std::vector<std::size_t> dims; /**< 各维长度。 */
Type type; /**< 元素类型。 */
LayoutClass layout = LayoutClass::Compact;
std::vector<std::uint8_t> data; /**< 解码后的原始字节(行优先,按 `dims`)。 */
std::uint64_t chunkAddress = 0; /**< 分块数据集的 B-tree 地址。 */
std::vector<std::size_t> chunkDims;
bool deflate = false; /**< 是否使用了 deflate(zlib)过滤器。 */
bool ok = false;
};
/** @brief 打开文件(一次性读入内存;`.mat` 通常几十 MB,可接受)。 */
bool Open(const std::wstring& path, std::wstring& error);
void Close();
/** @brief 根分组地址。 */
std::uint64_t Root() const { return m_root; }
/** @brief 该对象是否为数据集(有数据空间消息)。 */
bool IsDataset(std::uint64_t address) const;
/** @brief 分组(或结构体)的成员:`名字 → 对象头地址`。 */
std::vector<std::pair<std::string, std::uint64_t>> Members(std::uint64_t address) const;
/** @brief 按名字取成员(不存在返回 0)。 */
std::uint64_t Member(std::uint64_t address, const std::string& name) const;
/** @brief 读取数据集定义(含解码后的原始数据)。 */
bool ReadDataset(std::uint64_t address, Dataset& out) const;
/** @brief 读取字符串属性(如 `MATLAB_class`、`MATLAB_fields`)。 */
std::string AttributeString(std::uint64_t address, const char* name) const;
/** @brief 读取 `char` 数据集为宽字符串(MATLAB 的 char 是 uint16)。 */
bool ReadText(std::uint64_t address, std::wstring& out) const;
/** @brief 读取数值数据集为 double 序列。 */
bool ReadNumbers(std::uint64_t address, std::vector<double>& out) const;
/** @brief 读取引用数据集(HDF5 对象引用 = 对象头地址,相对基地址)。 */
bool ReadReferences(std::uint64_t address, std::vector<std::uint64_t>& out) const;
private:
struct Message
{
std::uint16_t type = 0;
std::vector<std::uint8_t> data;
};
std::uint8_t U8(std::uint64_t offset) const;
std::uint16_t U16(std::uint64_t offset) const;
std::uint32_t U32(std::uint64_t offset) const;
std::uint64_t U64(std::uint64_t offset) const;
std::uint64_t Addr(std::uint64_t offset) const;
std::uint64_t Resolve(std::uint64_t address) const;
void ReadMessages(std::uint64_t address, std::vector<Message>& messages) const;
bool ParseType(const std::uint8_t* data, std::size_t size, std::size_t offset, Type& out,
std::size_t& consumed) const;
bool DecodeChunked(const Dataset& dataset, std::size_t elementSize,
std::vector<std::uint8_t>& out) const;
void WalkBtree(std::uint64_t node, std::uint64_t heapData,
std::vector<std::pair<std::string, std::uint64_t>>& out) const;
void WalkSymbolNode(std::uint64_t node, std::uint64_t heapData,
std::vector<std::pair<std::string, std::uint64_t>>& out) const;
std::vector<std::uint8_t> m_data;
std::uint64_t m_base = 0;
std::uint64_t m_root = 0;
int m_offsetSize = 8;
int m_lengthSize = 8;
mutable std::vector<std::pair<std::uint64_t, std::uint64_t>> m_heaps; /**< 堆地址 → 数据地址。 */
};
} // namespace jdi
#endif // __MAT_H5_HPP__