组名平滑悬浮 + 单点信号初始视图 + 光标吸附统一判定(自动点线同源)
一、信号列表「组名悬浮」平滑化、且不再遮住下一个组名
* 悬浮位置改为 min(列表上边界, 本组最后一行下边界 − 组名高度):
- 本组还有行在列表里时钉在列表上边界;
- 本组最后一行开始滑出上边界时,组名跟着一起上移、随滚动条自然离界
(不再像旧实现那样突然消失);
- 「本组最后一行的下边界」恰好就是下一个分组行的自然位置,因此悬浮组名的下边界
永远不超过下一组名 —— 旧实现固定钉在列表上边界会把下一组名盖住,让别人以为
后面的信号属于上一组。
* 折叠的分组不再悬浮(折叠时本组“最后一行”就是它自己,悬浮位置 = 自然位置,
于是随滚动自然划过,不会停在最上方)。
* 底部边界线只在真的“浮”在其它行之上时才画,避免与正常绘制出现像素差。
二、单点信号的初始视图
* 新增 PlotView::FitSinglePointSignal():当前没有任何信号被勾选、而勾选的第一个信号
只有一个有效数据点时,把该数据点居中放置,时间跨度 = 1s、Y 轴跨度 = 1
(各为一个大格),并关闭该网格的整段自适应以免被覆盖。
Application::SynchronizeView 在首次勾选时先尝试它,失败才走 FitAll()。
* AutoRangeLaneToView 的退化区间(可见区间内只有一个取值)改用与 PadRange 一致的留白
max(1, |v|×0.1),不再产生“几乎为 0”的区间(那会让幅值网格线挤成一团、看不出数据点)。
* 需求作废:上一轮“2…16 个有效采样点默认改为点线 + 2px”已取消(放大自动点线已覆盖该场景);
只保留“只有一个有效采样点 → 点线 + 最大线宽”。
* SignalData::singlePointTime 升级为 sparseTimes/sparseValues(有效点 ≤ 1024 时在加载/插入
时缓存全部有效点),同时服务单点初始视图与光标吸附。
三、光标吸附的统一判定
* N = GridMinorCellCount():网格底衬一横排的小格数,与网格绘制用同一套
NiceStep + 五等分算出(因此不随缩放等级大幅变化)。
* SignalSnapEnabled():当且仅当某信号在当前一屏内的数据点个数 < N×4 时为它开启吸附;
判据随视界实时重算 ⇒ 疏密变化的信号在稀疏区自动吸附、移回稠密区自动自由移动。
额外要求视界内至少有一个可见数据点(否则“0 < N×4”会把数据全在视界之外的信号
误判为稀疏)。统计只在可见下标区间内进行且攒够阈值即早退,代价有界。
* SnapTimeToSample():判定只针对鼠标所在网格(同一个时间轴上不同网格结果可以不同);
同一网格内所有符合判定的信号的数据点都参与吸附比较;
吸附半径 = 一个网格小格的宽度,且不超过半个局部采样间距 ⇒ 数据点很稀疏
(一屏只有一两个点)时光标在点之间仍可自由移动(吸附是提示,不是卡住)。
* 删除旧的 SampleSpacingPixels(6px 下门限 + 半屏宽上门限)。
四、放大自动切点线改为与吸附判定完全同源
* UpdateAutoScatterStyles() 改用 SignalSnapEnabled:进入判定自动切「点线」,
不再满足(适应视图、或视界从稀疏区移到稠密区)自动恢复原样式;
自动期间用户手动改过则不恢复(尊重用户选择,判定放在恢复之前)。
* 删除旧的 AxisSparseInView(“采样间距 > 半屏宽”)。
五、验证(离屏 .preflight/check:SW_HIDE 窗口、不注入真实鼠标键盘、进程/线程 BELOW_NORMAL)
55/55 通过(连跑 3 次稳定):[A] 组名悬浮(钉住/平滑上移/整组滚过后换下一组/折叠组不悬浮/
悬浮下边界不超过下一组名)、[B] 单点初始视图(跨度 1s 与 1、居中、点线 + 最大线宽、
两信号时不再走单点路径)、[C] 吸附判定(满视图都关闭/稀疏窗口开启/无可见点不开启/
同一时间轴不同网格结果不同/同网格多信号的点都参与/稠密↔稀疏双向动态切换/
单点靠近吸附远离自由)、[D] 自动点线(同源判定/恢复/用户手动改过不覆盖)。
A/B:临时禁用「悬浮位置公式 / 单点初始视图 / 吸附判定」后重跑 ⇒ 14 项如实失败。
make release 零警告(用户正运行 Target.exe,链接用临时 -o 名字验证通过)。
六、规则同步
Documents/agent_mem.md 新增 §9.30、§11.2 第 58–61 条硬约束与变更日志。
This commit is contained in:
1 parent
9f6de3bba4
commit
57cc714c1d
8 files changed
+808
-230
No files matched your search
+109
-8
@@ -1579,6 +1579,89 @@ GDI+ 抗锯齿逐段扫描这些**近竖直、满幅**的边,代价爆炸(
|
||||
|
||||
---
|
||||
|
||||
### 9.30 组名平滑悬浮 + 单点信号初始视图 + 光标吸附统一判定(含自动点线同源)(本轮,起源设备)
|
||||
|
||||
> 本轮四项(用户 2026-10-15 第二轮报告):① 组名消失太突兀、且悬浮组名会盖住下一个组名
|
||||
>(让人误以为后面的信号属于上一组);② 只有一个数据点的信号“全局自适应不生效”、
|
||||
> 初始视图看不出它的状态;③ 光标吸附体验差且经常错误吸附,改用“一屏数据点 < N×4”的统一判定;
|
||||
> ④ 放大后自动切点线没按预期工作,改为**与吸附判定完全同源**。
|
||||
> 离屏验证 **55/55 通过**(连跑 3 次稳定),A/B 禁用三项机制后 **14 项如实失败**。
|
||||
|
||||
#### 一、组名悬浮:平滑滚出 + 不遮挡下一组(`SignalPanel`)
|
||||
|
||||
- 悬浮位置由 `StickyGroupRowTop(row, rowCount)` 给出:
|
||||
**`min(列表上边界, 本组最后一行下边界 − 组名高度)`**。
|
||||
* 本组还有行在列表里 ⇒ 钉在列表上边界;
|
||||
* 本组最后一行开始滑出上边界 ⇒ 组名跟着一起上移、随滚动条**自然离界**(不再突然消失);
|
||||
* 因为“本组最后一行的下边界”恰好就是**下一个分组行的自然位置**,所以悬浮组名的下边界
|
||||
永远不超过下一个分组行 ⇒ **不可能遮住下一组名**(旧实现固定钉在 `m_listRect.Y` 会盖住它)。
|
||||
- **折叠的分组不悬浮**:折叠时本组的“最后一行”就是它自己那一行,于是悬浮位置 = 它的自然位置
|
||||
⇒ 随滚动自然划过(旧实现会让它停在最上方遮住下一组)。
|
||||
- 底部边界线只在“真的浮在其它行之上”(`stickyTop > 自然位置`)时才画,避免与正常绘制有像素差。
|
||||
- 常规行循环仍必须**跳过**被悬浮的那一行(否则同一行画两次)。
|
||||
|
||||
#### 二、单点信号的初始视图(`PlotView::FitSinglePointSignal`)
|
||||
|
||||
- 触发条件(用户明确要求):当前**没有任何信号被勾选**、勾选的第一个信号**只有一个有效数据点**
|
||||
⇒ 旧路径(`FitAll` 的整段自适应)会让时间轴覆盖整条时间列、Y 轴退化成几乎为 0 的区间。
|
||||
- 新行为:数据点**居中**,**时间跨度 = 1s**、**Y 轴跨度 = 1**(各为“一个大格”);
|
||||
同时设 `autoRange = false`、`fitYToSelection = false`(否则会被自适应路径改回去)。
|
||||
- `Application::SynchronizeView` 在首次勾选时先试 `FitSinglePointSignal()`,失败才 `FitAll()`。
|
||||
- `AutoRangeLaneToView` 的**退化区间**(可见区间里只有一个取值)也改为 `PadRange` 同款留白
|
||||
`max(1, |v|×0.1)`,不再给“几乎为 0”的区间(那会让幅值网格线挤成一团)。
|
||||
- ⚠️ **需求作废**:上一轮“有效采样点 2…16 的信号默认改成点线 + 2px”**已取消**
|
||||
(用户 2026-10-15 明确作废:下面第四项的“放大自动点线”已覆盖那个场景)。
|
||||
只保留“**只有一个**有效采样点 → 点线 + 最大线宽”这一条。
|
||||
- `SignalData::singlePointTime` 已升级为 **`sparseTimes` / `sparseValues`**
|
||||
(有效点 ≤ `kSparseTimesLimit = 1024` 时在加载/插入时缓存全部有效点,按时间升序),
|
||||
同时服务于:单点初始视图(取时刻与取值)、吸附判定(二分)、最近数据点查找。
|
||||
|
||||
#### 三、光标吸附的统一判定(`PlotView`)
|
||||
|
||||
- **N = `GridMinorCellCount()`**:网格底衬一横排的小格数,与 `PaintLane`/`PaintTimeAxis`
|
||||
用**同一套** `NiceStep(span, kTimeTickTarget)` + 五等分算出(所以不随缩放等级大幅变化)。
|
||||
- **`SignalSnapEnabled(axis, signal)`**(用户 2026-10-15 的定义):
|
||||
**当且仅当**该信号在**当前一屏**内的数据点个数 `< N × 4` 时开启吸附;
|
||||
判据随视界实时重算 ⇒ 疏密变化的信号“移到稀疏区自动吸附、移回稠密区自动自由移动”。
|
||||
⚠️ 额外要求视界内**至少有一个可见数据点**(否则“0 < N×4”会把“数据全在视界之外”的信号
|
||||
误判成稀疏,白切一次点线且没有任何可吸附对象)。
|
||||
⚠️ 性能:总数不足阈值时直接返回(配合 `sparseTimes` 二分);否则只统计可见下标区间内的
|
||||
有限值并**攒够阈值即早退**,扫描窗口还有 `threshold × 64` 的上限(防 NaN 长循环)。
|
||||
- **`SnapTimeToSample`**:判定**只针对鼠标所在网格**(`LaneAtY(m_mouseY)`)——
|
||||
同一个时间轴上,一个网格达到稀疏标准、另一个没有时,只在前者吸附;
|
||||
同网格内**所有符合判定的信号**的数据点都参与比较。
|
||||
**吸附半径 = 一个网格小格的宽度**(与判定同源),且**不超过半个局部采样间距**
|
||||
⇒ 数据点很稀疏(一屏只有一两个点)时光标在点之间仍可自由移动(吸附 ≠ 卡住)。
|
||||
⚠️ `LocalSampleSpacing` 对 `finiteCount <= 1` 必须返回 0(“数据点间距无定义”),
|
||||
否则会拿时间轴的采样间隔充数(NaN 填充的轴上即 1ms)把半径压到极小、单点永远吸不住。
|
||||
⚠️ 旧实现(`SampleSpacingPixels` 的“下门限 6px + 上门限半屏宽”)**已删除**,不要再恢复。
|
||||
|
||||
#### 四、自动点线:与吸附判定**完全同源**(`UpdateAutoScatterStyles`)
|
||||
|
||||
- 只有 `SignalSnapEnabled` 为真的信号自动切「点线」;不再满足时(适应视图、或视界从稀疏区
|
||||
移到稠密区)自动恢复原样式;自动期间用户手动改过则不恢复(用户选择优先)。
|
||||
- ⚠️ “用户是否手动改过”的判定必须在恢复判断**之前**(用户改完可能紧接着就退出阈值)。
|
||||
- `AxisSparseInView`(旧的“采样间距 > 半屏宽”)**已删除**。
|
||||
|
||||
#### 五、验证(离屏 `.preflight/check`,`SW_HIDE` + 不注入真实输入 + 进程/线程 `BELOW_NORMAL`)
|
||||
|
||||
- **55/55 通过**(连跑 3 次稳定):`[A]` 组名悬浮(钉住 / 平滑上移 / 整组滚过后换下一组 /
|
||||
折叠组不悬浮 / 悬浮下边界不超过下一组名)、`[B]` 单点初始视图(跨度 1s 与 1、居中、
|
||||
样式为点线 + 最大线宽、两个信号时不再走单点路径)、`[C]` 吸附判定(满视图都关闭 /
|
||||
稀疏窗口开启 / 无可见点不开启 / 同时间轴不同网格结果不同 / 同网格多信号的点都参与 /
|
||||
稠密↔稀疏双向动态切换 / 单点靠近吸附远离自由)、`[D]` 自动点线(同源判定 /
|
||||
恢复 / 用户手动改过不覆盖)。
|
||||
- **A/B**:临时禁用「悬浮位置公式 / 单点初始视图 / 吸附判定」后重跑 ⇒ **14 项如实失败**。
|
||||
- ⚠️ 测试手法坑(本轮踩到并修掉):① 每个用例前 `ClearGroups`,否则行号/网格号不确定;
|
||||
② **测试夹具的时间列必须单调升序** —— 否则时间轴被判为非单调、`VisibleIndexRange` 退化成整段,
|
||||
于是“稀疏窗口”里会把视界外的稠密点也数进去(本轮的 `mixed` 信号就是这样被误判的);
|
||||
③ `AddGroup` 会 push 进 `vector<SignalGroup>`,之前缓存的 `SignalData*`/`TimeAxis*`
|
||||
引**用可能失效**,用完要重新 `Resolve`/`Axis`;
|
||||
④ 改完 `PlotView.cpp` 后要确认 `make release` 真的重编译了(曾遇时间戳未更新而链接到旧 `.o`,
|
||||
症状恰好等于“修复没生效”——先 `(Get-Item …).LastWriteTime = Get-Date` 再 make)。
|
||||
|
||||
---
|
||||
|
||||
## 10. 变更日志(本文件维护记录)
|
||||
|
||||
| 日期 | 设备/模型 | 摘要 |
|
||||
@@ -1621,6 +1704,7 @@ GDI+ 抗锯齿逐段扫描这些**近竖直、满幅**的边,代价爆炸(
|
||||
| 2026-10-15 | DeepSeek-V4.1-Flash(**另一台设备**) | **数据层大重构(文件→分组 / 时间轴一等公民 / 加载器抽象)+ CSV 多时间轴 + MF4 支持(详见 §9.27 与 `Documents/refactor_report_2026-10-15.md`)**。① **解耦**:`DataFile`→**`SignalGroup`**(删除 `filePath`/`fileName`/单一 `time`/`timeColumnName`/`timeMonotonic`/`minTime`/`maxTime`),新增 **`TimeAxis`**(1..N 条/组)与 `SignalData::axisIndex`,`SignalData` 删除冗余的 `rawHeader` 与**全仓无人使用的 `unit`**;`SignalRef::fileIndex`→`groupIndex`;`DataSet`/`TabConfig` 全套改名并新增 `Axis(ref)`;采样/统计 API(`SampleSignalAt`/`FindNearestSample`/`VisibleIndexRange`/`EdgeIndexRange`/`EdgeCountsFor`/`EdgeCenterCountsFor`/`EdgeScanFor`/`MeanFilterValueAt`/`WeakPeakValueAt`/`SampleSpacingPixels`)**首参由 `const DataFile&` 改为 `const TimeAxis&`**;`ResolvedSignal` 携带 `{group, axis}`。② **加载器抽象**:新增 `DataTable.hpp`(`DataTable`/`LoadProgress`/`TableBlock`/`LoadedSource`)、`DataLoader.*`(唯一入口 `LoadSignalSource` 按扩展名分发)、`CsvLoader.*`、`Mf4Loader.*`,删除 `CsvTable.*`;唯一装配点 `BuildSignalGroup` 与格式无关(新增格式只写加载器)。③ **CSV 多时间轴**:每个 `time` 列开启新块、各块按自己的时间列裁剪 ⇒ 支持各轴不等长(实测 3 条轴 7/4/7),**不再把其它 `time` 列当成信号**;时间轴判据收紧。④ **MF4 支持**:加载后新建分组、组名=文件名,全文件信号归入该组;每个通道组(CG)一条时间轴并自动去重(实测 27 CG → 3 轴 / 124 信号);实现 MDF v4 块解析(DG/CG/CN/CC/DT)、位级整数与 32/64 位浮点解码、线性换算 `phys = cc_val[0] + cc_val[1]*raw`、无效位处理;**`##TX` 兼容规范的「长度+UTF-16」与 CANape 的「裸 NUL 结尾文本」**(否则通道名全空)。⑤ 顺带修复:`Application::Initialize` 在窗口类已注册时不再弹模态框(旧实现导致同进程二次构造永久阻塞),`Shutdown` 注销窗口类;UI 文案统一到「分组」,对话框支持 `*.mf4/*.mdf`。验证:新增 `datacheck` **69/69**、`edgecheck` **46/46**(替代被编码破坏的 `panelcheck`)、既有 `verify` **47/47**(另 2 条本机旧规则断言)、`make release` 零警告。 |
|
||||
| 2026-10-15 | DeepSeek-V4.1-Flash(**起源设备**) | **信号列表滚动越界绘制 + 边沿统计面板「显示冻结」(详见 §9.28)**。① **列表把组名/信号名画到筛选框上**(MF4 多信号场景):根因是 `UiText` 走 GDI `DrawTextW`,**GDI 文本不遵循 GDI+ 的 `SetClip`** —— 被滚出视口的「部分可见」行会把文字画到列表上方的筛选框上(`UiFillRect` 的大面积不透明矩形走 GDI `FillRect` 同理)。修法:`PaintUtil` 新增 **`UiTextClipped`**(同一 HDC `SaveDC` + `IntersectClipRect` + `RestoreDC`;`UiText` 转发 `clip=nullptr`,行为不变),`SignalPanel` 新增 `PaintRowText`(裁到 `m_listRect`)/ `FillRowRect`(夹到 `m_listRect`),分组行与信号行的全部文本与行底色均改走它们。② **边沿统计面板在不移动鼠标时显示冻结**:面板像素图层缓存键(`PanelSlotKey`)**不含**面板上显示的统计数字,而统计是**分帧推进**的中间结果 ⇒ 推进不会让键变化 ⇒ 面板一直贴旧像素 ⇒ 显示停在「统计中…」/中间值,**只有移动鼠标**(改变面板层序/悬停/鼠标坐标等其它键)才重绘并显示早已算完的正确结果(扫描推进本身没停)。修法:`PanelSlotKey` 内按面板实际显示的条目回调 `EdgeCountsFor`/`EdgeCenterCountsFor` 并把 `(rising, falling, complete)` mix 进键。验证:新增离屏 `.preflight/check`(`SW_HIDE` + 不注入真实输入 + `BELOW_NORMAL`)**39/39 通过**(连跑两次稳定):`[A]` 40 信号滚轮下滚后「列表上方区域逐像素 0 变化」且「列表内部仍有变化」(修复前 **1571 像素被污染**)、`[B]` 3 信号 × 300 万采样下两种统计面板的**缓存键持续变化**且**无鼠标输入即完成**(修复前键不变化)、`[C]` 真实 `MF4-Record.MF4`(**124 信号 / 3 条时间轴**)同样 0 像素变化;**并做了 A/B**(`git stash` 回退修复后 `[A]/[B]` 如实失败);`make release` 零警告。 |
|
||||
| 2026-10-15 | DeepSeek-V4.1-Flash(**起源设备**) | **全局 Y 轴自适应跟随网格结构 + 组名悬浮 + 稀疏信号默认点线 + 单点光标吸附 + 放大自动点线(详见 §9.29)**。① **新网格不继承全局 Y 轴自适应**:`RebuildLanes` 对新出现的信号返回默认 `LaneCarry()` ⇒ `fitYToSelection=false`;修法:重建前先算 `previousAllAutoFit`(非空且**每个**网格都开着 = 全局开启的正式定义),`LaneCarry` 加 `matched` 标记,新信号用它当默认;并新增 `ReapplyGlobalAutoFitIfEnabled()` 在 **`Layout()`(网格数变化)/`RefreshLanes()`(信号数变化)/`SplitLane()`** 之后立即重算 ⇒ 新勾选(按**当前视界**自适应)、取消显示(剩余信号重新收敛)、拆分(每份各自自适应)三项全覆盖。② **组名悬浮**:新增 `SignalPanel::StickyGroupRowIndex()` + `PaintStickyGroupRow()`(不透明底 + 底部边界线),`RowRect` 加 `m_rowYOverrideActive/Y` 覆盖行顶部(行内所有几何的唯一来源);判据 = 分组行已在列表上边界之上且本组最后一行仍在列表内;常规循环**跳过**被钉住的行(否则画两次)。③ **稀疏信号默认样式**:新增 `ApplySparseDefaultStyle`(1 个有效点 → 点线 + **最大线宽 10px**;2..16 → 点线 + 2px),在 `BuildSignalGroup`/`InsertDerivedSignal` 两处调用;线宽常量统一到 `DataModel.hpp`(`kSignalLineWidthMin/Max`)并被 SignalPanel 滑块引用;`ComputeStats` 输出 `firstFiniteIndex`、`SignalData` 新增 `singlePointTime`。④ **单点光标吸附**:`SnapTimeToSample` 新增半径分支(`kCursorSnapPointRadiusPixels=8` 逻辑像素)吸附 `singlePointTime`,半径外自由移动(**吸附而非卡住**)。⑤ **放大自动点线**:`SignalStyle` 新增 `autoScatterActive/autoScatterSavedMode/autoScatterUserOverride`,`AxisSparseInView`(间距 > 半个绘图区宽度,与吸附上门限同源,**单点轴不算稀疏**)+ `UpdateAutoScatterStyles()`(在 `SetTimeView` 等四处调用)⇒ 进入阈值自动切点线、退出阈值恢复原样式,**用户手动改过则不恢复**(判定必须放在恢复判断**之前**,否则用户改完紧接着缩出去会被覆盖)。验证:新增离屏 `.preflight/check` **65/65 通过**(连跑 3 次稳定,覆盖 7 组断言;含“钉住行与自然位置逐像素一致”“单点吸附半径内外”“用户手动改样式后退出不覆盖”),**A/B**:临时禁用六个机制后 **21 项如实失败**;`make release` 零警告。 |
|
||||
| 2026-10-15 | DeepSeek-V4.1-Flash(**起源设备**) | **组名平滑悬浮 + 单点信号初始视图 + 光标吸附统一判定(自动点线同源)(详见 §9.30)**。① **组名悬浮平滑化**:位置改为 `StickyGroupRowTop = min(列表上边界, 本组最后一行下边界 − 组名高度)` ⇒ 组名随最后一行**自然滚出**(不再突然消失),且下边界永不越过下一个分组行的自然位置 ⇒ **不再遮住下一组名**(旧实现固定钉在列表上边界会把下一组名盖住,让别人以为后面的信号属于上一组);**折叠的分组不悬浮**(随滚动自然划过);底部边界线只在真的“浮”起来时才画。② **单点信号初始视图**:新增 `PlotView::FitSinglePointSignal()`(无任何信号被勾选、勾选的第一个信号只有 1 个有效点时生效)—— 数据点居中、**时间跨度 1s**、**Y 跨度 1**,并置 `autoRange=false`/`fitYToSelection=false`;`Application::SynchronizeView` 首次勾选时先试它、失败才 `FitAll()`;`AutoRangeLaneToView` 的退化区间改为 `PadRange` 同款留白 `max(1,|v|×0.1)`。③ **吸附判定统一**(用户给出的唯一判据):新增 `GridMinorCellCount()`(网格一横排小格数 N,与网格绘制同源)+ `SignalSnapEnabled()`(**当且仅当**某信号在当前一屏内的数据点 `< N×4` 才开启吸附,随视界实时重算 ⇒ 疏密信号自适应),`SnapTimeToSample` **只按鼠标所在网格**判定、同网格内所有符合判定的信号数据点都参与、吸附半径 = 一个网格小格宽且不超过半个局部采样间距(吸附≠卡住);旧的 `SampleSpacingPixels`(6px 下门限+半屏宽上门限)**已删除**。④ **自动点线改为与吸附判定完全同源**(`UpdateAutoScatterStyles` 调 `SignalSnapEnabled`),旧 `AxisSparseInView` **已删除**;进入阈值自动切点线、不再满足自动恢复、用户手动改过则不恢复。⑤ **需求作废**:“2…16 个有效采样点默认点线 + 2px”**已取消**(只保留“1 个有效点 → 点线 + 最大线宽”);`SignalData::singlePointTime` 升级为 **`sparseTimes`/`sparseValues`**(有效点 ≤ 1024 时全部缓存,供吸附与单点视图使用)。验证:新增离屏 `.preflight/check` **55/55 通过**(连跑 3 次稳定;覆盖组名悬浮四种场景、单点初始视图、吸附判定/网格作用域/多信号/动态切换/单点半径、自动点线同源与用户覆盖);**A/B** 禁用三项机制 ⇒ **14 项如实失败**;`make release` 零警告。 |
|
||||
|
||||
---
|
||||
|
||||
@@ -1838,14 +1922,31 @@ GDI+ 抗锯齿逐段扫描这些**近竖直、满幅**的边,代价爆炸(
|
||||
(名称框 / ✕ / 徽标)的唯一来源,因此「组名悬浮」只需给它加一个顶部坐标覆盖
|
||||
(`m_rowYOverrideActive/m_rowYOverrideY`)即可整体跟随;并且**常规行循环必须跳过被钉住的那一行**,
|
||||
否则同一行会被画两次(一次在滚动位置、一次在顶部)。
|
||||
58. ⚠️ **「少数数据点」信号的默认样式**(用户明确要求):有效采样点 **1 个** → 「点线」+
|
||||
**最大线宽(10px)**(单点就是一个小圆点,半径与线宽成正比);**2 … 16 个** → 「点线」+ 2px;
|
||||
其余保持「线段」+ 1px。只作用于**新建**信号(不覆盖用户设置)。
|
||||
⚠️ 线宽范围常量(`kSignalLineWidthMin/Max`)必须**单一来源**(`DataModel.hpp`),
|
||||
信号面板滑块要引用它,否则「最大线宽」会在两处漂移。
|
||||
⚠️ 单点信号的光标吸附是**半径判据**(`kCursorSnapPointRadiusPixels = 8` 逻辑像素),
|
||||
半径外必须自由移动 —— 是「吸附」不是「卡住」;其时刻由 `SignalData::singlePointTime`
|
||||
在加载时缓存(不要改成鼠标移动时线性扫描采样)。
|
||||
58. ⚠️ **光标吸附的判定只有一套定义(§9.30)**:某信号在当前一屏内的数据点个数
|
||||
`< N × 4`(N = `GridMinorCellCount()`,网格一横排的小格数,与网格绘制同源)时
|
||||
才为它开启吸附;判据**随视界实时重算**(疏密变化的信号自动在稀疏区吸附、稠密区自由)。
|
||||
⚠️ 额外要求视界内**至少有一个可见数据点**;
|
||||
⚠️ 判定**只针对鼠标所在网格**(`SnapTimeToSample` 用 `LaneAtY(m_mouseY)`);
|
||||
⚠️ 吸附半径 = **一个网格小格宽**,且**不超过半个局部采样间距**(吸附 ≠ 卡住光标);
|
||||
⚠️ 旧的 `SampleSpacingPixels`(6px 下门限 + 半屏宽上门限)**已删除,不要再恢复**。
|
||||
⚠️「放大自动切点线」必须**与吸附判定完全同源**(`UpdateAutoScatterStyles` 调
|
||||
`SignalSnapEnabled`),不要再引入“采样间距 > 半屏宽”之类的独立判据。
|
||||
⚠️ `SignalData` 用 **`sparseTimes` / `sparseValues`**(有效点 ≤ 1024 时缓存全部有效点)
|
||||
支撑吸附与单点初始视图;**不要**在鼠标移动时扫描整条采样数组。
|
||||
⚠️ `LocalSampleSpacing` 对 `finiteCount <= 1` 必须返回 0(数据点间距无定义),
|
||||
否则单点信号永远吸不住。
|
||||
59. ⚠️ **「2…16 个有效采样点用点线 + 2px」的需求已作废**(用户 2026-10-15 明确取消)。
|
||||
仍保留:**只有一个有效采样点** → 「点线」+ **最大线宽**;其余信号保持默认「线段 + 1px」。
|
||||
需要“提示数据点位置”的场景一律由 §9.30 的“放大自动点线”承担(同源判定)。
|
||||
60. ⚠️ **单点信号的初始视图**:当前无任何信号被勾选、而勾选的第一个信号只有**一个**有效数据点时,
|
||||
必须走 `FitSinglePointSignal()`(数据点居中、**时间跨度 1s**、**Y 轴跨度 1**),
|
||||
不要再落到 `FitAll()` 的整段自适应(时间轴会覆盖整条时间列、Y 区间退化成几乎为 0)。
|
||||
同时 `AutoRangeLaneToView` 的退化区间要按 `PadRange` 同款留白 `max(1, |v|×0.1)`。
|
||||
61. ⚠️ **组名悬浮的位置公式(勿改回“固定钉在列表上边界”)**:
|
||||
`min(列表上边界, 本组最后一行下边界 − 组名高度)` ⇒
|
||||
组名随最后一行**平滑滚出**(不突然消失),且下边界永不越过下一个分组行的自然位置
|
||||
⇒ **不可能遮住下一个组名**;**折叠的分组不悬浮**(随滚动自然划过)。
|
||||
行几何仍只由 `RowRect()` 提供(用 `m_rowYOverrideActive/m_rowYOverrideY` 覆盖顶部坐标)。
|
||||
|
||||
### 11.3 离屏验证方法学(本机偏好,强烈建议沿用)
|
||||
|
||||
|
||||
Reference in new issue
Block a user