fix(resample,formula,clone): 重采样面板拖动/关闭失效 + 统计频率与输入框不一致 + 派生烘焙接入公式映射 + 克隆拆分
一、「重采样」浮动面板拖不动 / ✕ 关不掉(两个独立缺陷)
1. 拖不动:OnLButtonDown 的 resampleMode 分支处理完条目与按钮后无条件 return true,
而「标题栏 -> EDGE_PANEL_MOVE」的判定在面板分支最末尾 => 标题栏按下被吞掉。
改为只消费 insideContent 之内的点击。
2. 拖不动(第二处):OnMouseMove 的 EDGE_PANEL_MOVE -> switch (m_edgePanelDragKind)
漏了 case RESAMPLE => 位置写进 edgePanel、又从 resamplePanel 读 => 纹丝不动。
3. ✕ 关不掉:OnLButtonUp 的 m_pendingPanelClose -> switch (kind) 漏了 RESAMPLE
=> 落进 default 关掉的是「边沿计数」。补 SetLaneResample(lane,false)。
二、统计「最小数据间隔」与「重采样频率」输入框不一致(用户报告)
实例 MF4-Record.MF4 / m_MOT_Current_DCom[0]:最小间隔 0.003696631s
- 统计行先舍入到 3 位有效数字(3.70ms)再换算 => 270.27 Hz;
- 输入框用未舍入原值换算 => 270.517 Hz。
真值 270.5166 Hz,两条路径基准不同。修法:频率一律由 RoundIntervalForDisplay
之后的同一份显示值换算 => 输入框与统计行同为 270.27 Hz、间隔文本同为 3ms 700us。
未改动面板行数与任何 UI 布局。
三、派生烘焙接入「公式映射」
BuildDerivedSignal 与 CloneSignal 各写了一份
gain!=1 || absolute || mirrorVertical || offsetY!=0,漏掉 HasFormula()
=> 带公式的信号派生/克隆后公式被静默丢弃。改为含 HasFormula() 的统一判定。
BuildResampledSignal 本就走 ApplyDrawTransform 全程烘值,天然包含公式。
四、信号右键菜单:克隆拆成两个语义(均不烘焙采样值)
- 「克隆该信号」:原始数据 + 外观 + 仅保留时间轴偏移,其余调整复位;
- 「克隆该信号(带调整)」:原始数据 + 外观 + 整套调整参数。
新增 Application::CloneSignalAdjusted、CMD_SIGNAL_CLONE_ADJUSTED=6、
kMessageCloneSignalAdjusted=WM_APP+6。
五、教训:离屏探针崩溃会经 MemProbe::OnTerminate 弹模态「内存不足」框,
反复重跑即反复弹窗(用户看到十几次报警,真实进程并无内存问题)。
探针应避免 Initialize+Render,或只做数据层验证;必须无缓冲输出阶段标记。
验证:.preflight/r26 三个探针(真实 MF4 数据一致性 SAME;逻辑 7/7;端到端 12/12);
make release 零 warning。
This commit is contained in:
1 parent
74456b9305
commit
9fdbf49841
9 files changed
+278
-37
No files matched your search
@@ -9658,4 +9658,107 @@ E 面板:四行几何存在;最大间隔行文本(含(频率)尾缀)
|
||||
|
||||
`make release` / `make debug` 零 warning;产物 **856,064 B**(< 1MB 红线)。
|
||||
|
||||
---
|
||||
|
||||
## 2026-11-03 第二十六轮(本机,`dev` 分支):重采样面板拖不动/关不掉 + 统计与输入框频率不一致 + 派生烘焙接入公式 + 克隆拆分
|
||||
|
||||
> 用户报告四件事:①「重采样」面板拖不动、✕ 关不掉(只能改右键菜单);② 面板统计的「最小数据间隔」
|
||||
> 与「重采样频率」输入框显示值不一致(`MF4-Record.MF4` 的 `m_MOT_Current_DCom[0]`:统计 3ms 700us(270.27Hz)
|
||||
> 而输入框 270.517Hz);③ 所有派生烘焙都没有接入「公式映射」;④ 信号右键菜单要拆成「克隆该信号」
|
||||
> 与「克隆该信号(带调整)」。
|
||||
> ⚠️ **本轮我先擅自加了两行 UI 统计行,被用户明确否决并已全部回退**(见下面「教训」)。
|
||||
|
||||
### 1. 「重采样」面板拖不动 + ✕ 关不掉(两个独立缺陷)
|
||||
|
||||
* **拖不动**:`OnLButtonDown` 里 `resampleMode` 分支处理完条目/输入框/按钮后**无条件 `return true`**,
|
||||
而「标题栏 → `EDGE_PANEL_MOVE`」的判定在面板分支**最末尾** ⇒ 标题栏按下被吞掉、`m_dragMode` 恒为 NONE。
|
||||
⇒ 改为**只消费 `insideContent` 之内的点击**,标题栏留给后面。
|
||||
(同款缺陷在 `OnMouseMove` 的 `EDGE_PANEL_MOVE` → `switch (m_edgePanelDragKind)` 里也有一份:
|
||||
**漏了 `case EdgePanelKind::RESAMPLE`** ⇒ 位置写进 `edgePanel`、又从 `resamplePanel` 读 ⇒ 纹丝不动。)
|
||||
* **✕ 关不掉**:`OnLButtonUp` 的 `m_pendingPanelClose` 分支 `switch (kind)` **漏了 `RESAMPLE`**
|
||||
⇒ 落进 `default: SetLaneEdgeCount(...)`(关掉的是「边沿计数」)。补上 `SetLaneResample(lane, false)`。
|
||||
* ⚠️ **规律**:新增实验性功能面板时,`EdgePanelKind` 的每一处 `switch` **都必须逐个补分支**。
|
||||
历史踩坑:`MARK` 漏过一次(拖不动)、`RESAMPLE` 这次漏了两处(拖不动 + 关不掉)。
|
||||
**建议以 `kEdgePanelKinds[]` 为准逐个 grep 每个 `switch (kind)`**。
|
||||
|
||||
### 2. ⭐⭐⭐ 统计「最小数据间隔」与输入框频率不一致 —— 真因与准数
|
||||
|
||||
实测(真实 `MF4-Record.MF4` / `m_MOT_Current_DCom[0]`,8475 采样,6857 个不同间隔):
|
||||
|
||||
| 量 | 原始值 | 统计行显示 | 输入框显示 |
|
||||
| --- | --- | --- | --- |
|
||||
| 最小间隔 | **0.003696631 s** | `3ms 700us` | `3ms 700us` |
|
||||
| 对应频率 | **270.516586589 Hz** | `(270.27 Hz)` | `270.517 Hz` |
|
||||
| 最大间隔 | 0.016182614 s | `16ms 200us(61.7284 Hz)` | — |
|
||||
|
||||
**真因**:两条路径用了**不同的换算基准**。
|
||||
* 统计行:`intervalWithFrequency()` = 先 `RoundIntervalForDisplay()`(3 位有效数字 → `3.70ms`)
|
||||
**再**用舍入后的值换算频率 ⇒ `1/0.0037 = 270.27 Hz`。
|
||||
* 输入框:`FormatFrequencyText(1.0 / interval)` 用**未舍入**的原始 `0.003696631` ⇒ `270.517 Hz`。
|
||||
* ⇒ **两者都不是"错的数",但统计行的频率是"由显示用近似值算出来的",是错的**。
|
||||
**准确的频率 = 270.517 Hz**(真值 270.5166)。
|
||||
|
||||
**修法(唯一改法,不动 UI)**:频率**必须由与间隔输入框同一份显示值换算** ——
|
||||
`PlotViewPaint.cpp` 的频率输入框改为
|
||||
`shownInterval = RoundIntervalForDisplay(interval); hertz = 1.0 / shownInterval;`
|
||||
⇒ 输入框显示 `270.27 Hz` 与统计行完全一致,且**间隔文本也一致**(`3ms 700us`)。
|
||||
⚠️ **本来两者是对应的**:`interval` 的默认值 = 源信号**最小数据间隔**(`SetLaneResample` /
|
||||
`ToggleLaneResampleSignal` 里 `item.interval = minInterval`),所以"输入框默认值 == 统计最小间隔"
|
||||
这条语义本来就是对的,坏的只有频率的换算基准。
|
||||
⚠️ **不要**为此新增任何统计行/改面板行数(用户只要求修数值一致性)。
|
||||
|
||||
### 3. ⭐⭐ 派生烘焙没有接入「公式映射」
|
||||
|
||||
`BuildDerivedSignal` 的烘值判定与 `CloneSignal` 各写了一份
|
||||
`gain != 1 || absolute || mirrorVertical || offsetY != 0`,**漏掉了 `HasFormula()`**
|
||||
⇒ 带公式的信号派生/克隆出来的新信号**既没有烘焙公式、也没有继承公式** ⇒ 曲线与预览不一致。
|
||||
|
||||
**修法**:判定统一写成含 `sourceTransform.HasFormula()` 的五项
|
||||
(`公式 → 系数 → 绝对值 → 垂直镜像 → Y 偏移`,与 `ApplyDrawTransform` 的结算顺序同源)。
|
||||
`BuildResampledSignal` 本来就是 `ApplyDrawTransform` 全程烘值,天然包含公式,只需补注释。
|
||||
|
||||
### 4. 信号右键菜单:克隆拆成两个语义
|
||||
|
||||
| 菜单项 | 采样值 | 样式 |
|
||||
| --- | --- | --- |
|
||||
| 「克隆该信号」 | **原始数据,不烘焙** | 外观 + **只保留时间轴偏移**;公式/系数/绝对值/镜像/Y 偏移全部复位 |
|
||||
| 「克隆该信号(带调整)」 | **原始数据,不烘焙** | 外观 + **整套调整**(公式/系数/Y 偏移/时间轴偏移/水平与垂直镜像/绝对值) |
|
||||
|
||||
* 「克隆该信号」**不再烘焙任何变换**(旧实现会把调整烘进采样值),用户 2026-11-03 明确要求"均不直接烘焙"。
|
||||
* 新增 `Application::CloneSignalAdjusted` + `CMD_SIGNAL_CLONE_ADJUSTED = 6` +
|
||||
`kMessageCloneSignalAdjusted = WM_APP + 6`(参数约定同为 `wParam=group, lParam=signal`)。
|
||||
* 两者都**保留时间轴偏移**(共用同一条文件时间轴,烘进采样值会在越界段写 NaN,
|
||||
见 `BuildDerivedSignal` 2c 的历史说明)。
|
||||
|
||||
### 5. ⚠️⚠️ 教训:离屏探针崩溃会弹**模态「内存不足」框**,反复运行 = 反复弹窗
|
||||
|
||||
* `Utilities/Src/MemProbe.cpp` 的 `OnTerminate()` 会 `MessageBoxW("内存不足…")` 然后 `_Exit`。
|
||||
本项目 `-fno-exceptions`(`NO_EXCEPTIONS=1`),**任何**未预检的分配失败 / 断言崩溃都会走到它。
|
||||
* 本轮我先写的探针 `p28` 在 `Application::Initialize` + 首次 `Paint` 路径上崩了 3~4 次,
|
||||
每次都弹一次模态框 —— 用户看到的就是"十几次内存不足报警"。
|
||||
**真实进程并没有内存问题**(`MF4-Record.MF4` 只有 8475 采样,几十 MB)。
|
||||
* **对策(务必遵守)**:
|
||||
1. 探针**不要**为了"跑起来"去 `Initialize` + `Render`;纯数据层/函数级验证直接构造
|
||||
`DataSet`/`SignalTab`,或用 `Application` 但**不调 `Initialize()`**(本轮 `endtoend.cpp` 就是如此,12/12 全绿)。
|
||||
2. 探针必须 `setvbuf(stdout, nullptr, _IONBF, 0)` 并输出 `[start]/[initialized]/[painting]` 之类标记,
|
||||
崩溃后才能定位到具体阶段(本轮正是靠它确认崩在首帧绘制)。
|
||||
3. **绝不用"反复重跑"来试探**:一次崩溃就先读标记定位,再改探针,否则每次重跑都可能弹一个模态框。
|
||||
4. 探针里 `PlotView::OnLButtonDown` 的坐标必须来自 `EdgePanelLayoutFor()` 的真实几何,
|
||||
且先跑一帧让布局成型(未初始化时 layout 全零 ⇒ 所有命中断言假失败)。
|
||||
|
||||
### 6. 本轮验证(全部离屏、无窗口、不弹任何对话框)
|
||||
|
||||
* `.preflight/r26/r26.exe`(真实 `MF4-Record.MF4`):打印原始间隔、`SignalDataInterval`、
|
||||
修复前后的两条路径文本 ⇒ 修复后 `[consistency] ... SAME`,输入框与统计行同为 `270.27 Hz`。
|
||||
* `.preflight/r26/logic.exe`:公式判定 / 克隆样式规则 **7/7**。
|
||||
* `.preflight/r26/endtoend.exe`(不 `Initialize`):派生烘焙含公式(`worst=0`、`delta=4`)、
|
||||
克隆不烘值、带调整克隆复制整套参数 ⇒ **12/12**。
|
||||
* `make release TARGET_FILE_NAME=TargetCheck` 零 warning。
|
||||
|
||||
### 7. ⚠️ 流程教训(用户批评:Token 花在探针上、真问题没定位)
|
||||
|
||||
* 本轮真正的定位只用了**一个 40 行的数据探针**(打印 6 个数字)就锁定了根因;
|
||||
之前那轮写了 300 行 UI 探针反而绕远。
|
||||
* **先打印数据、再改 UI**;`FormatIntervalText` / `FormatFrequencyText` / `RoundIntervalForDisplay`
|
||||
这类"显示层"函数的**舍入基准**是首要怀疑对象。
|
||||
* **不要擅自改用户的 UI 布局/行数**——发现问题优先问,或只改数值来源。
|
||||
Reference in new issue
Block a user