Files
Just-DataInspector/Utilities/Inc/FormulaEval.hpp
T
DeepSeek-V4.1-Flash 74456b9305 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。
2026-11-03 02:55:22 +08:00

64 lines
2.6 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#ifndef __FORMULA_EVAL_HPP__
#define __FORMULA_EVAL_HPP__
// 「公式映射」的表达式编译与求值(信号样式区的 `Y = …` 输入框)。
//
// 语法(刻意保持极小,避免引入脚本引擎与额外的体积):
// expr := term (('+' | '-') term)*
// term := unary (('*' | '/') unary)*
// unary := ('+' | '-') unary | power
// power := primary ('^' unary)? —— 右结合:2^3^2 = 2^(3^2)
// primary := number | '(' expr ')' | variable | constant
// number := 十进制小数(支持 e/E 指数)
// constant:= `e`(自然常数)| `pi`(圆周率)—— 大小写均接受,保存前统一为小写
// variable:= `X` / `x` / `f(t)` —— 都表示"当前采样点的值"(**不支持单独的 `f`**)
//
// ⚠️ 求值走 `thread_local` 的单条缓存(按 `std::wstring` 的 data 指针 + 内容判定):
// 绘制循环里同一条信号的样式对象是稳定的,因此每个信号只需编译一次;
// **绝不能**每个采样点都重新解析(千万级采样会慢到不可用)。
#include <string>
namespace jdi
{
/**
* @brief 校验公式文本是否可以解析。
*
* 空文本是合法输入(表示"不做映射")。
*
* @param [IN] text 公式文本(不含 `Y =` 前缀)。
* @param [OUT] error 解析失败时的原因(成功时清空),允许为空。
* @return 语法合法返回 true。
*/
bool ValidateFormula(const std::wstring& text, std::wstring* error);
/**
* @brief 计算公式在给定采样值处的取值。
*
* 文本为空或无法解析时**原样返回** `x`(保证绘制不会因为非法公式而消失)。
* 计算结果非有限(如 1/0)时返回 NaN(保持断线语义)。
*
* @param [IN] text 公式文本。
* @param [IN] x 采样值(公式中的 `f(t)` / `X`)。
* @return 映射后的值。
*/
double EvaluateFormula(const std::wstring& text, double x);
/**
* @brief 把用户输入里的自变量写法统一成 `f(t)`、常量统一为小写(保存前调用)。
*
* 规则:
* * 把独立的 `X` / `x` / `f(t)`(含 `F(T)`)记号改写为 `f(t)`;
* * 把 `E` / `Pi` / `PI` 等写法改写为小写 `e` / `pi`;
* * **单独的 `f` 不改写**(它不是合法写法,留给校验报错)。
*
* 只做记号级替换(不会动标识符中间或数字里的字符,如 `1e5` 保持不变)。
*
* @param [IN] text 用户输入的公式。
* @return 归一化后的文本。
*/
std::wstring NormalizeFormulaText(const std::wstring& text);
} // namespace jdi
#endif // __FORMULA_EVAL_HPP__