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:
1 parent
955b233e63
commit
74456b9305
13 files changed
+682
-74
No files matched your search
@@ -346,6 +346,72 @@ namespace jdi
|
||||
return std::wstring(buffer);
|
||||
}
|
||||
|
||||
std::wstring FormatDecimal(double value, int significantDigits)
|
||||
{
|
||||
if (std::isnan(value))
|
||||
{
|
||||
return L"NaN";
|
||||
}
|
||||
if (std::isinf(value))
|
||||
{
|
||||
return (value > 0.0) ? L"+Inf" : L"-Inf";
|
||||
}
|
||||
if (significantDigits < 1)
|
||||
{
|
||||
significantDigits = 1;
|
||||
}
|
||||
if (significantDigits > 15)
|
||||
{
|
||||
significantDigits = 15;
|
||||
}
|
||||
const double magnitude = std::fabs(value);
|
||||
if (magnitude == 0.0)
|
||||
{
|
||||
return L"0";
|
||||
}
|
||||
|
||||
// 小数位数 = 让结果保留约 `significantDigits` 位有效数字(整数部分已超出时取 0 位)。
|
||||
// ⚠️ **绝不用 `%g` / `%e`**:`%g` 在量级小于 1e-4 或大于 1e6 时会切换成科学计数法,
|
||||
// 对“时间间隔 / 频率”这类读数极不友好(用户 2026-11-03 明确要求小数形式)。
|
||||
int decimals = 0;
|
||||
{
|
||||
const double exponent = std::floor(std::log10(magnitude));
|
||||
decimals = significantDigits - 1 - static_cast<int>(exponent);
|
||||
}
|
||||
if (decimals < 0)
|
||||
{
|
||||
decimals = 0;
|
||||
}
|
||||
if (decimals > 12)
|
||||
{
|
||||
decimals = 12;
|
||||
}
|
||||
|
||||
wchar_t buffer[160];
|
||||
swprintf(buffer, sizeof(buffer) / sizeof(buffer[0]), L"%.*f", decimals, value);
|
||||
std::wstring text(buffer);
|
||||
|
||||
// 去掉尾随的 0 与孤立的小数点(`1.50000` → `1.5`、`2.000000` → `2`)。
|
||||
if (text.find(L'.') != std::wstring::npos)
|
||||
{
|
||||
std::size_t last = text.find_last_not_of(L'0');
|
||||
if (last == std::wstring::npos)
|
||||
{
|
||||
return L"0";
|
||||
}
|
||||
if (text[last] == L'.')
|
||||
{
|
||||
--last;
|
||||
}
|
||||
text.erase(last + 1);
|
||||
}
|
||||
if (text == L"-0")
|
||||
{
|
||||
text = L"0";
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
std::wstring FormatDurationCompound(double seconds)
|
||||
{
|
||||
if (!std::isfinite(seconds))
|
||||
@@ -406,12 +472,12 @@ namespace jdi
|
||||
{
|
||||
const double whole = std::floor(value);
|
||||
const double fraction = value - whole;
|
||||
std::wstring text = FormatValue(whole) + L"s";
|
||||
std::wstring text = FormatDecimal(whole) + L"s";
|
||||
const double remainderMs = fraction * 1000.0;
|
||||
if (remainderMs >= 1.0e-6)
|
||||
{
|
||||
text += L" ";
|
||||
text += FormatValue(remainderMs);
|
||||
text += FormatDecimal(remainderMs);
|
||||
text += L"ms";
|
||||
}
|
||||
return text;
|
||||
@@ -435,7 +501,7 @@ namespace jdi
|
||||
// 顶级单位为 ps:不再递进次级单位(保留小数)。
|
||||
if (index + 1 >= subCount)
|
||||
{
|
||||
return FormatValue(value / subUnits[index].scale) + L" " + subUnits[index].name;
|
||||
return FormatDecimal(value / subUnits[index].scale) + L" " + subUnits[index].name;
|
||||
}
|
||||
|
||||
const double scaled = value / subUnits[index].scale;
|
||||
@@ -456,7 +522,7 @@ namespace jdi
|
||||
if (inNext >= 1.0e-6)
|
||||
{
|
||||
text += L" ";
|
||||
text += FormatValue(inNext);
|
||||
text += FormatDecimal(inNext);
|
||||
text += subUnits[index + 1].name;
|
||||
}
|
||||
return text;
|
||||
@@ -468,7 +534,8 @@ namespace jdi
|
||||
{
|
||||
return L"0";
|
||||
}
|
||||
return FormatValue(std::fabs(seconds));
|
||||
// ⚠️ 内联编辑的数值**不得出现科学计数法**(用户 2026-11-03 要求)。
|
||||
return FormatDecimal(std::fabs(seconds), 12);
|
||||
}
|
||||
|
||||
std::wstring FormatFrequency(double hertz)
|
||||
@@ -494,7 +561,9 @@ namespace jdi
|
||||
unit = L"kHz";
|
||||
scaled = hertz / 1.0e3;
|
||||
}
|
||||
return FormatValue(scaled) + L" " + unit;
|
||||
// ⚠️ 频率同样不得出现科学计数法(用户 2026-11-03 要求):
|
||||
// `FormatValue` 的 `%.6g` 在 1e-5 Hz 这类量级会输出 “1e-06”,改为十进制形式。
|
||||
return FormatDecimal(scaled) + L" " + unit;
|
||||
}
|
||||
|
||||
std::wstring FormatByteSize(unsigned long long bytes)
|
||||
|
||||
Reference in new issue
Block a user