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:
1 parent
1e1c2432ae
commit
955b233e63
3 files changed
+127
-6
No files matched your search
@@ -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);
|
||||
|
||||
@@ -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` 找不到"第一个可见信号"、面板布局为空。
|
||||
|
||||
Reference in new issue
Block a user