5b24dacee0757c9efa154d75ebc01fe2bf8050bc
3
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
5b24dacee0 |
悬停高亮恢复旧版外延观感 + 支持 MATLAB .mat(v7.3 / HDF5,嵌套分组)
一、悬停高亮「几乎看不到、只是曲线换了颜色」(对比提交 |
||
|
|
34375481de |
网格底衬永不空白 + 动画流畅性根治 + 光标随首个信号重现 + 「放大自动点线」新判据
一、网格底衬消失(半屏/局部/全屏,且要等一会才刷新好)—— 两个机制叠加 1) BlitWideBuffer 对「跨度已过期」的图层原用 StretchBlt 放大占位:深度放大时把一小段像素放大 几十倍 ⇒ 整块网格变成一片无网格的模糊色块(用户看到的就是「网格叠底消失、没有任何内容」)。 修法:删除占位拉伸;对拿不到(完整)图层像素的网格【就地补画网格底衬】 (新增 PaintLaneGridBacking + m_renderGridOnly,复用 PaintLane 的网格部分,跳过波形/散点/ 派生曲线/Y 轴刻度/图例)。底衬代价极低,可每帧执行 ⇒ 网格永不空白/闪烁。 ⚠️ 顺序:先画底衬(GDI+)再 g.GetHDC() 做 BitBlt;只有全部网格都贴不出像素时才交给整幅重绘。 2) 待重画队列按下标顺序消费(一次 1 个)⇒ 上百个网格时可视网格要等上百帧。 修法:RenderPendingLaneRebuilds 里做稳定划分,【可视网格优先】(LaneVisuallyRelevant); 「结算」判据由 LanesAllReadyForView() 改为 LanesVisiblyReadyForView()。 二、过渡动画卡顿/不平顺/直接闪到终态 —— 三处根因 1) 「m_layerRebuildPending 非空就每帧请求再送一帧」:上百个网格时 700ms 内挤出 124 次重绘, 而 WM_TIMER 优先级低于投递消息 ⇒ 动画定时器被饿死(实测 frames=124 / timerTicks=1)。 修法:新增 AnyVisibleLanePending(),只用「还有没有可视网格待补」决定是否请求再送一帧; 离屏网格改为机会式预热(实测 frames=25 / timerTicks=16)。 2) SetTimer 的间隔会被量化到系统 tick(默认 15.6ms):请求 16ms 实际等到下一个 tick ⇒ 实测帧距 ~26ms(只有约 38fps)。修法:kScrollTimerPeriodMs 16 → 10(实测 ~15.5ms ≈ 64fps; 真实消息循环帧距 ~25ms → ~16ms)。实测映射:8→15.8 / 10→15.5 / 12→16.5 / 16→26.4 / 31→37.9。 ⚠️ timeBeginPeriod(1) 在本机无效(已 A/B 验证)。 3) 网格滚动联动 / 「回到最上方」每帧都改 LaneScroll(),而它原本参与「宽基底可用性」判据 (SceneGeometryHash)⇒ 每一帧都退化成整幅重绘。修法:SceneContentHash 新增 includeLaneScroll 形参, 宽基底可用性判据不计网格滚动量/各网格屏幕纵向位置(它们只是图层的贴图落点); 静态场景缓存的指纹仍然计入。实测网格滚动动画单帧 18.36ms → 1.19ms。 三、光标工具激活且没有任何信号绘制时,首个信号出现后按规则重新出现(用户要求) 新增 PlotView::ResetCursorPlacementOnFreshContent()(规则与 CycleCursorMode() 的「首次出现」同源: 单光标置于视图中心;双光标第二条紧邻第一条且落在可见范围内),由 Application::SynchronizeView 的「首次出现可见信号」分支在 FitAll() 之后调用。 实测:单光标 stale=4.60 → 新的视图中心 20.92;双光标 stale=(2.89,7.18) → (20.92,26.07)。 四、「放大自动点线」规则改为:任一小格内数据点少于 5 个即自动切换(用户重新定义) 新增 PlotView::AutoPointLineNeeded()(推翻此前「一屏内数据点 < N×4」):按 GridMinorCellCount() 切成横向小格,只要存在任何一个小格内有效数据点 < kAutoPointLineMinPointsPerCell(=5) 就切「点线」。 实现:先「总有效点 < N×5」鸽巢 O(1) 充分判定;单调时间轴逐小格两次二分取区间(单格累计到 5 个即早退, 另给 kAutoPointLineCellScanCap=512 封顶);非单调时间轴一次有界扫描分桶。保留「视界内至少一个可见点」保护。 ⚠️ 光标吸附仍用旧的整屏判据 SignalSnapEnabled(用户明确要求「其它规则不变」)。 验证(全部离屏,不占用鼠标/键盘/屏幕) - 新增 .preflight/perf/gridanim_main.cpp(gridanim.exe)55/55: [A] 缩放/适应视图/「预热未完成再滚动」等每个用例逐帧断言「每个可视网格都有网格底衬 (有竖直锐边、无模糊占位、无未绘制带)」;[B] 直接驱动 + 真实消息循环两条路径统计每帧耗时、 帧距均值/最大与进度单调性;[C] 光标重现;[D] 自动点线新判据(稠密不切、稀疏切换、恢复原样式)。 - 既有回归:verify 49/49、panelanim 156/156、datacheck 69/69、edgecheck 46/46、errcheck 19/19。 - make release 零错误零警告(使用 TARGET_FILE_NAME=TargetCheck 以避免与用户正在运行的实例冲突)。 文档:Documents/agent_mem.md 新增 §9.42、§11.2 第 117–124 条与变更日志一行。 |
||
|
|
e6b7bf66ec |
按组件目录结构把四个巨型实现文件拆分为多个协作文件(PlotView / SignalPanel / Application / DataModel)
承接上一条提交(0871eb0)—— 该提交只完成了 Components/Shared 与注释/命名清理,并**主动未做**
大文件拆分;本提交把大文件拆分补齐,至此“按功能拆分逻辑块到各组件自己的 Inc/Src”已落地。
## 一、拆分结果
| 原文件 | 拆分后 |
| --- | --- |
| Components/PlotView/Src/PlotView.cpp(13 695 行 / 544 KB) | PlotView.cpp(787) + PlotViewPaint.cpp(6 367) + PlotViewInteraction.cpp(2 715) + PlotViewFeatures.cpp(3 005) + PlotViewInternal.hpp(936) |
| Components/SignalPanel/Src/SignalPanel.cpp(4 184 行) | SignalPanel.cpp(1 141) + SignalPanelPaint.cpp(697) + SignalPanelInteraction.cpp(2 095) + SignalPanelInternal.hpp |
| Components/Application/Src/Application.cpp(3 135 行) | Application.cpp(1 427) + ApplicationCommands.cpp(1 184) + ApplicationLoading.cpp(270) + ApplicationInternal.hpp |
| Utilities/Src/DataModel.cpp(2 615 行) | DataModel.cpp(1 583,含自由函数区) + DataModelTab.cpp(732) + DataModelInternal.hpp |
分片职责(以 PlotView 为例):
core 视图核心:构造/析构、上下文、布局、坐标映射、视界与平移、网格结构与归属
paint 渲染与像素缓存:全部绘制、场景/装饰缓存、每网格图层、宽基底与滚动补条
interaction 交互与编辑:命中测试、鼠标/键盘、光标形状、内联输入框、浮动面板层序
features 视图特性与派生数据:光标读数/提示/缩放、自适应、吸附、单点、派生曲线线程池、
边沿统计、实验性功能参数
(SignalPanel 为 core/paint/interaction;Application 为 core/commands/loading;
DataModel 为 core/tab。)
## 二、关键手法(这是能安全拆开的前提)
原文件的辅助区都是**匿名命名空间**(PlotView 约 870 行常量与工具函数),被所有成员函数广泛引用。
直接拆文件会让各 .cpp 拿不到这些工具,因此:
1. 把该块整块搬进 `<组件>Internal.hpp`,从 `namespace { }` 改为**具名**命名空间
(pvdetail / spdetail / appdetail / dmdetail);
2. 块内**每个函数与常量都标 inline** ⇒ 多个翻译单元共用同一个定义:既不重复符号,
也不产生 N 份函数定义;
3. 头部末尾 `using namespace <ns>;` ⇒ 各 .cpp 函数体里原有的非限定调用(IntersectRect /
NiceStep / ClampDouble …)**一行都不用改**;
4. 成员定义按「方法名 → 分片」归类后**整块搬移** —— 全程**纯代码搬移**,没有改动任何一行函数体。
## 三、拆分过程中踩到并修正的四个坑(已固化到 .preflight/perf/split_component.ps1)
1. `[[maybe_unused]]` 等属性**必须排在 inline 之前**(`inline [[x]] T` 非法);
单独的 `{`/`}` 行、注释行、`struct`/`enum`/`using`/`template` 行不能加 inline。
2. 匿名命名空间的**结束行可能带注释**(`} // namespace`),只认 `^\t}$` 会一路匹配到后面
某个成员函数的结束括号,把大段成员定义误当成“辅助区”搬走。
3. **首个成员定义之前的所有行**必须跟着进核心文件:DataModel.cpp 在匿名命名空间之后、
首个成员定义之前还有约 390 行命名空间外的自由函数(SignalStats / ComputeStats /
CacheSparseSamples / ApplySinglePointDefaultStyle …),漏掉会被静默丢弃。
4. 内部头必须带上原文件真正用到的全部头:DataModel 需要 Theme.hpp / <cstring> / <cstdio> /
<deque> / <atomic> / <cwctype> / <new>(原文件靠这些头才编译得过)。
## 四、附带完成的命名审计
PlotView 中残留的 File 语义命名一并修正:LaneSpansMultipleFiles → LaneSpansMultipleGroups、
firstFile → firstGroup、multiFile → multiGroup、SceneCacheKey::haloFile → haloGroup。
## 五、验证(全部离屏:SW_HIDE + 不注入真实输入 + 进程/线程 BELOW_NORMAL)
- .preflight/perf/verify.exe(PlotView 渲染/交互,含逐像素断言)49/49
- .preflight/perf/panelanim.exe(信号面板:卷尺式折起/展开、筛选态、悬浮组名、目标行)156/156
- .preflight/perf/datacheck.exe(数据层:单轴/多时间轴 CSV、MF4、重命名语义)69/69
- .preflight/perf/edgecheck.exe(边沿统计与面板排版)46/46
- make release / make debug 零警告。
即:拆分后**行为与拆分前完全一致**(纯代码搬移,四套探针全绿)。
## 六、体积(诚实记录)
Target.exe: 754 176 → 764 416 B(+10 240 B,+1.4%)
因为 inline 工具函数会在每个引用它的翻译单元里各生成一份(PE/COFF 链接器没有 ICF 合并)。
这再次印证:拆分解决的是**可维护性**,不是体积(本轮的 −24.6% 全部来自 ICO 256×256 条目改 PNG)。
## 七、文档
Documents/agent_mem.md:§9.37 新增“七、多文件拆分(已完成)”与“八、仍未完成”(只剩
Lane → LanePanelState、模态提示解耦、MF4 压缩块三项);§11.2 新增第 91、92 条(多文件化的
唯一安全手法 + 体积代价)。Documents/refactor_report_2026-10-16.md 同步更新 §五/§七/§八。
|