feat(style): 「系数/偏移」改为「映射/偏移」并新增「公式映射」

- 按钮文案改为「映射/偏移」;激活态 = 公式非空 或 系数≠1 或 任一偏移≠0
- 二级面板顶部新增「公式映射」(标题栏带还原图标):Y = + 表达式输入框,默认 f(t)
- 结算顺序固定:公式映射 -> 系数 -> 绝对值 -> 垂直镜像 -> Y 偏移 -> 时间轴偏移
- 表达式规则:支持 + - * / ( ) 一元正负、常量 pi、自变量 X/x/f/f(t);保存前把 X 归一化为 f(t)
- 非法表达式不写入样式,警告走 SignalPanel::TakePendingHint -> PlotView::ShowPersistentHint(自绘浮层,不用 MessageBox)
- 公式参与折线缓存键 / SceneContentHash / SceneStyleEntry / HaloLayerCache;
  DrawTransformOrderPreserving 返回 false(非保序),自适应幅值在 HasFormula() 时逐点扫描
- 派生信号与克隆的烘焙自动包含公式(时间轴偏移除外)
- Documents/agent_mem.md 追加「第二十四轮」硬约束
This commit is contained in:
DeepSeek V4.1 Flash committed 2026-11-02 20:42:58 +08:00
1 parent 1e1c2432ae
commit 955b233e63
3 files changed
+127 -6

No files matched your search

+66 -3
View File
@@ -20,12 +20,32 @@ namespace jdi
return false;
}
// 二级面板:两张“标题(带还原图标)+ 输入框”的组合:
// 1. 「系数调整」:一整行系数输入框;
// 2. 「偏移调整」:下方两行分别为「Y轴」与「时间轴」的偏移输入框。
// 二级面板:三组“标题(带还原图标)+ 输入框”的组合:
// 1. 「公式映射」:`Y =` + 表达式输入框(自变量写作 f(t));
// 2. 「系数调整」:一整行系数输入框;
// 3. 「偏移调整」:下方两行分别为「Y轴」与「时间轴」的偏移输入框。
//(与网格 Y 轴上下限的菜单内联编辑同一套做法:提交后面板保持打开,可连续微调。)
// ⚠️ 结算顺序固定为「公式映射 → 系数 → 偏移」(用户 2026-11-02 明确要求)。
std::vector<MenuItem> items;
// 「公式映射」:默认 `f(t)`(= 等值照搬)。
MenuItem formulaHeader;
formulaHeader.kind = MenuItemKind::HEADER_ACTION;
formulaHeader.label = L"公式映射";
formulaHeader.id = CMD_FORMULA_RESET; // 点击还原图标 → 恢复为 f(t)
items.push_back(formulaHeader);
const std::wstring formulaText =
style->formula.empty() ? std::wstring(L"f(t)") : style->formula;
MenuItem formulaEdit;
formulaEdit.kind = MenuItemKind::EDIT;
formulaEdit.label = L"Y ="; // 输入框独占右侧剩余整行
formulaEdit.id = CMD_FORMULA_EDIT;
formulaEdit.value = formulaText;
formulaEdit.editValue = formulaText;
formulaEdit.filter = EditFilter::ANY;
items.push_back(formulaEdit);
MenuItem gainHeader;
gainHeader.kind = MenuItemKind::HEADER_ACTION;
gainHeader.label = L"系数调整";
@@ -337,6 +357,35 @@ namespace jdi
const bool valid = !trimmed.empty() && stop != trimmed.c_str() && std::isfinite(parsed);
switch (result.id)
{
case CMD_FORMULA_EDIT:
{
// 「公式映射」:把自变量写法统一为 `f(t)`,非法表达式**不写入**并给出警告
//(警告走自绘浮层,见 `TakePendingHint`;不使用 Windows 消息框)。
const std::wstring normalized = NormalizeFormulaText(trimmed);
if (normalized.empty())
{
style->formula.clear();
}
else
{
std::wstring error;
if (!ValidateFormula(normalized, &error))
{
m_pendingHint = L"公式无法解析:" + error;
}
else
{
style->formula = normalized;
}
}
if (m_menu != nullptr)
{
const std::wstring display =
style->formula.empty() ? std::wstring(L"f(t)") : style->formula;
m_menu->UpdateItemValue(CMD_FORMULA_EDIT, display, display);
}
return false;
}
case CMD_GAIN_EDIT:
{
if (valid)
@@ -391,6 +440,20 @@ namespace jdi
}
}
// 标题栏右侧的「还原」图标:公式恢复为默认的 `f(t)`(= 等值照搬)。
if (result.id == CMD_FORMULA_RESET)
{
if (SignalStyle* style = StyleOf(SelectedRef()))
{
style->formula.clear();
if (m_menu != nullptr)
{
m_menu->UpdateItemValue(CMD_FORMULA_EDIT, L"f(t)", L"f(t)");
}
}
return false;
}
// 标题栏右侧的「还原」图标:系数重置为默认值 1(面板保持打开,输入框立即回显)。
if (result.id == CMD_GAIN_RESET)
{
@@ -904,9 +904,10 @@ namespace jdi
UiText(g, label, m_fonts->uiSmall, rect,
checked ? m_theme->textStrong : m_theme->text, TextAlign::CENTER);
};
// 系数/偏移:系数不等于默认值 1、或任一偏移不为 0 时显示为"已启用"。
paintToggle(kAdjustGain, L"系数/偏移",
!GainIsDefault(style->gain) || style->Offset(), StyleHit::GAIN_ADJUST);
// 映射/偏移:公式映射非空、系数不等于默认值 1、或任一偏移不为 0 时显示为"已启用"。
paintToggle(kAdjustGain, L"映射/偏移",
style->HasFormula() || !GainIsDefault(style->gain) || style->Offset(),
StyleHit::GAIN_ADJUST);
// 镜像:水平或垂直任一被勾选即显示为激活态。
paintToggle(kAdjustMirror, L"镜像", style->Mirrored(), StyleHit::MIRROR_ADJUST);
paintToggle(kAdjustAbsolute, L"绝对值", style->absolute, StyleHit::ABSOLUTE_TOGGLE);
+57
View File
@@ -9498,3 +9498,60 @@ make release / make debug 零 warning;Release 814,080 B
`paintOverlays()` 里,否则只会在整幅重绘的那一帧出现。
* ⚠️ PowerShell 5.1 的 `-replace`/多行字符串在含中文/引号时会把命令搞坏;改文件内容优先
用编辑工具或 `.ps1` + `[System.IO.File]::WriteAllText`。
## 2026-11-02 第二十四轮(本机,`dev` 分支):实验性功能「重采样」+ 样式区「映射/偏移」公式映射
> 本条为**硬约束**,后续改动若与本条冲突,以本条为准(远端合并时请保留并合并,不要覆盖)。
### A. 「重采样」(实验性功能,位于「自由标记」**上方**)
1. **数据模型**:`Lane::ResampleItem { ref, interval, initialized, maxInterval, minInterval, collapsed }`
+ `Lane::{resample, resampleItems, resamplePanel, resampleScroll}`;
`LaneCarry` / `SplitLane` / `PruneLaneSignalItems` / `RemapAfterSignalInsert` /
`RemapAfterSignalRemoval` **必须全部同步**(漏一处就会出现"拆网格后功能复制/条目指向错信号")。
2. **面板**:与「均值滤波 / 中心轴 / 弱峰调整」同一套**可滚动分组列表**(信号色叠底条目栏 +
右侧复选框 + 展开三角);展开后 4 行:最大数据间隔(只读)/ 最小数据间隔(只读)/
重采样间隔(输入,秒)/ 重采样频率(输入,Hz)。底部「构建为新信号」固定不滚动。
3. ⚠️ **最大/最小数据间隔绝不能在绘制时计算**:千万级信号逐帧扫描会卡死。勾选该信号时
(`SetLaneResample` / `ToggleLaneResampleSignal`)一次性算好并缓存进 `ResampleItem`,
绘制只读字段。
4. ⚠️ **重采样产生新的时间刻度** ⇒ 必须 `DataSet::AppendGroupAxis()` 追加一条新 `TimeAxis`,
再 `InsertDerivedSignal`(`axisIndex` 指向新轴)。**不能复用源信号的时间轴**(否则采样与时间错位)。
5. 采样值先按「公式映射 → 系数 → 绝对值 → 垂直镜像 → Y 偏移」变换(与绘制一致),
再在 `[t0, t1]` 上等间隔线性插值;**时间轴偏移不烘值**(留在新信号样式上,与其它派生信号同规则)。
6. 时间轴非单调 / 有效点 < 2 / 间隔 > 跨度 / 点数 > 2000 万 ⇒ **拒绝构建**并用
`PlotView::ShowPersistentHint` 提示(不弹 Windows 消息框)。
7. 新信号名 = `<原信号> (重采样 <频率>)`,频率单位在 Hz/kHz/MHz/GHz 自适应(`Utf8Util::FormatFrequency`)。
### B. 样式区「系数/偏移」→「映射/偏移」+ 公式映射
1. 按钮文案改为 **「映射/偏移」**;激活态 = 公式非空 **或** 系数 ≠ 1 **或** 任一偏移 ≠ 0。
2. 二级面板顶部新增 **「公式映射」**(标题栏带还原图标):`Y =` + 文本输入框,默认 `f(t)`。
复位 ⇒ `style->formula.clear()`(即以 `f(t)` 显示/生效)。
3. ⚠️ **结算顺序固定为:公式映射 → 系数 → 绝对值 → 垂直镜像 → Y 偏移 → 时间轴偏移**
(唯一实现在 `ApplyDrawTransform`,不要在别处重复实现公式)。
4. 表达式编译/求值唯一实现 = `Utilities/Inc|Src/FormulaEval.{hpp,cpp}`:
语法 `+ - * / ( )`、一元正负、数字(含 e/E 指数)、常量 `pi`、自变量 `X` / `x` / `f` / `f(t)`。
⚠️ 求值走 `thread_local` 单条缓存(按 `wstring.data()` 指针 + 内容判定),
**绝不逐采样点重新解析**(千万级采样会慢到不可用)。
5. 非法表达式:**不写入样式**,警告经 `SignalPanel::TakePendingHint` → 宿主
`m_plot.ShowPersistentHint()`(与"加载失败"同一套自绘浮层,**不用 MessageBox**)。
输入合法时把 `X`/`x`/`f` 归一化为 `f(t)` 再保存(`NormalizeFormulaText`)。
6. ⚠️ **公式会影响缓存与包围盒,四处必须同步**:
* `AcquirePolyline` 折线缓存键;
* `SceneContentHash`(→ 场景/宽基底缓存失效);
* `SceneStyleEntry`(→ 局部重画判据)+ `HaloLayerCache`(→ 悬停光晕);
* `DrawTransformOrderPreserving()` 返回 **false**(公式不保序,粗粒度包络层不能用);
自动幅值范围在 `HasFormula()` 时必须**逐点扫描**(`TabConfig::ComputeLaneRange`)。
7. 派生信号 / 克隆的烘焙自动包含公式(两者都走 `ApplyDrawTransform`),**时间轴偏移除外**。
### C. 验证(针对性,仅本轮功能)
`.preflight/devcheck`(`build.ps1` + `main.cpp`,**`.preflight` 已被 gitignore**)62/62:
公式解析/求值/归一化/结算顺序/非法回退、重采样端到端(新轴、5 点、插值精确、名称尾缀、
面板几何、默认勾选最小间隔)、`SignalDataInterval`、频率格式化、公式影响自适应量程。
⚠️ 链接 Build/Debug/*.o(含 LTO)**必须加 `-flto -flto-partition=one`**,否则 `binds_to_current_def_p` ICE。
⚠️ 探针里必须先 `Tab().SetVisible({0,0}, true)` 再开面板:新加载信号默认不勾选,
否则 `SetLaneResample` 找不到"第一个可见信号"、面板布局为空。