fix(formula,resample): 公式映射卡顿根治 + 语法/输入框完善 + 重采样面板三处修正

一、「公式映射」卡顿(用户报告:只是点进输入框就卡,编辑过公式后彻底没法用)
1. 真因:菜单内联编辑提交时无条件回写初值 ⇒ 点进输入框再点别处就把
   formula = "f(t)" 写进样式;而 HasFormula() 只判 !empty(),于是
   DrawTransformOrderPreserving() 变 false、ComputeLaneRange() 每帧逐点扫描
   整条信号(2580 万采样 ≈ 960ms/帧)。
   修法:新增 FormulaIsIdentity()(空串与 f(t) 等价);提交时归一化后恒等即清空;
   BuildLaneVisuals() 为「网格幅值范围」加指纹缓存(数据指针/长度/统计值 +
   影响数值的样式字段),键不变直接复用(实测 0.0005ms)。
2. 真实公式仍然贵:新增 PlotView::AcquireFormulaEnvelope()(变换后的逐分组
   包络,65536 组,按需算一次并缓存),BuildPolyline 的包络带改用它。
   实测:静止 215→21ms、中键拖动 8ms、缩放最坏 843→81ms;极值仍逐点统计
   (不会漏尖峰)。
3. AcquireProgram 只比 data() 指针 + 长度 ⇒ std::wstring 短串走 SSO 时,
   等长改公式会一直复用旧程序(f(t)^2→f(t)^3 波形不更新)。改为按内容判定。

二、公式语法(Utilities/FormulaEval)
- 新增 ^ 幂(右结合,2^3^2=512、2^-1=0.5、-2^2=-4),支持 2^f(t) 与 f(t)^2;
- 常量 e / pi(大小写均接受,保存前统一小写);
- 数据点只允许 X / x / f(t):单独的 f 不再接受(且不归一化,直接报错);
- 数字扫描修正:2e 里的 e 不再被当成指数吞掉。

三、「公式映射」输入框(SignalPanelMenu)
- Y= 画在输入框内部(非编辑态),进入编辑态只显示公式本身;
- label 留空 ⇒ 输入框占满整行,与下方「系数调整」同宽。

四、「重采样」面板
- 修复点进间隔/频率输入框后输入框整块消失(paintOverlays 的 PaintEditField
  名单漏了 RESAMPLE_INTERVAL / RESAMPLE_FREQUENCY);
- 最大/最小数据间隔:3 位有效数字 + Δt 复合单位,末尾追加(频率)尾缀;
- 间隔显示自适应单位、编辑态用秒;频率显示自适应单位、编辑态用 Hz(均不带单位);
- 面板宽度按含(频率)尾缀的文本量宽。

五、十进制格式化(Utf8Util)
- 新增 FormatDecimal(绝不产生科学计数法);FormatDurationCompound /
  FormatDurationSeconds / FormatFrequency 改用它,Δt/间隔/频率读数不再出现 1e-05。

验证:离屏探针 .preflight/perf/probe/formulacheck_main.cpp 75/75;
make release / make debug 零 warning;Release 856,064 B。
This commit is contained in:
DeepSeek-V4.1-Flash committed 2026-11-03 02:55:22 +08:00
1 parent 955b233e63
commit 74456b9305
13 files changed
+682 -74

No files matched your search

+51 -2
View File
@@ -194,6 +194,50 @@ namespace jdi
bool operator!=(const SignalRef& other) const { return !(*this == other); }
};
/**
* @brief 「公式映射」的**恒等**写法:`f(t)`(= 原样输出采样值)。
*
* 空串与它语义完全相同(都表示“不做映射”)。
*/
inline const wchar_t* const kIdentityFormulaText = L"f(t)";
/**
* @brief 判断公式文本是否**等价于不映射**(空串或 `f(t)`)。
*
* ⚠️ 恒等公式会被当作“未启用”:它逐点恒等,但若被当成“有公式”,
* `DrawTransformOrderPreserving()` 就会返回 false(丢掉粗粒度包络层),
* 且自适应幅值范围会退化成**逐点扫描整条信号** —— 千万级采样时每帧几百毫秒
* (用户 2026-11-03 报告的“只是点进过公式输入框就卡死”的直接原因)。
*
* @param [IN] formula 公式文本。
* @return 恒等(不做映射)返回 true。
*/
inline bool FormulaIsIdentity(const std::wstring& formula)
{
return formula.empty() || formula == kIdentityFormulaText;
}
/**
* @brief 公式输入框的**非编辑态**文本(内含 `Y=` 前缀,用户 2026-11-03 要求)。
* @param [IN] formula 公式文本(空 = 未启用)。
* @return 形如 `Y=f(t)` / `Y=f(t)^2 + 1` 的文本。
*/
inline std::wstring FormulaDisplayText(const std::wstring& formula)
{
return std::wstring(L"Y=") +
(FormulaIsIdentity(formula) ? std::wstring(kIdentityFormulaText) : formula);
}
/**
* @brief 公式输入框的**编辑态**文本(只有公式本身,不带 `Y=` 前缀)。
* @param [IN] formula 公式文本(空 = 未启用)。
* @return 形如 `f(t)` / `f(t)^2 + 1` 的文本。
*/
inline std::wstring FormulaEditText(const std::wstring& formula)
{
return FormulaIsIdentity(formula) ? std::wstring(kIdentityFormulaText) : formula;
}
/**
* @brief 单个信号的显示外观(颜色/透明度/线宽/绘制方式/数值变换)。
*
@@ -237,11 +281,16 @@ namespace jdi
* 结算顺序为 **公式映射 → 系数 → 绝对值 → 垂直镜像 → Y 偏移 → 时间轴偏移**,
* 见 `ApplyDrawTransform`。表达式由 `Utilities/FormulaEval` 编译(带缓存),
* 非法表达式的取值原样返回采样值(不会让波形消失)。
*
* ⚠️ **`f(t)`(恒等映射)与空串等价**:`HasFormula()` 两者都返回 false。
* 否则用户只是“点进输入框又点出去”就会让样式带上一个恒等公式,
* 而公式会关闭粗粒度包络层、逼得自适应量程每帧逐点扫描整条信号
* (千万级采样时界面直接卡死,用户 2026-11-03 报告)。
*/
std::wstring formula;
/** @brief 是否启用了「公式映射」。 */
bool HasFormula() const { return !formula.empty(); }
/** @brief 是否**真的**启用了「公式映射」(`f(t)` 恒等映射不算)。 */
bool HasFormula() const { return !FormulaIsIdentity(formula); }
/** @brief 镜像功能的任一分量是否启用(决定「镜像」按钮的激活态)。 */
bool Mirrored() const { return mirrorVertical || mirrorHorizontal; }