「调整」数值符号规则 + 边沿统计按帧推进(含首帧公平分摊)+ 浮动面板排版与弱峰复位控件

一、「调整」类数值的符号(SignalPanel)
1. FormatSignedNumber:0 不再显示 `+`(旧实现恒带符号 → `+0.00`),Y 轴偏移默认值显示 0.00。
2. FormatGainText:非 0 正值带 `+`、负值带 `-`、0 不带符号;仅用于非编辑态显示,
   进入编辑的初始文本仍为无符号纯数值。
3. 新增 FormatOffsetTimeText:`Δt:` 后按正负带符号(Δt:+5s 60ms / Δt:-5s 60ms / Δt:0s),
   打开面板 / CMD_OFFSET_TIME 提交 / CMD_OFFSET_RESET 三处统一走它。

二、边沿统计:按帧预算 + 每状态公平配额
(用户报告:按住横向光标不动时统计停在“统计中…”不更新;改中心轴参数回车后不动鼠标时停在部分值)
根因:预算按 GetTickCount64() 分桶,而系统时钟粒度约 15.6ms;RequestEdgeScanFrame 的
PostMessage 自持重绘把帧率拉高 ⇒ 同一 tick 内连画多帧却完全不推进。
1. Paint 开头 ++m_edgeScanFrame; m_edgeScanBudget = kEdgeScanPairsPerFrame;(按帧重置),
   删除 m_edgeScanTick。
2. 每个状态各自持有每帧配额(pumpedFrame/pumpedPairs):perState = max(60000, 1.2M/share),
   share = max(kEdgeScanFairShare=6, m_edgeScans.size())。
   注意:不能直接用 size() 分摊 —— 状态在同一帧内逐个创建,第一个那一刻 size()==1 会独占整额,
   同帧后面新建的状态全部被截断为 0 配额(即“只有第一条信号推进”)。
实测(合成 5 信号 × 200 万采样、同一网格):首帧 5 个状态全部推进;9 帧内全部扫完(期间不注入
任何鼠标消息);数值与独立参考实现逐位一致;阈值变化后就地重置、随后“停住不动”仍精确扫完。

三、浮动面板排版
1. 信号名宽度上限 kEdgePanelNameMaxWidth=150 逻辑像素:宽度计算与绘制都用 min(实测, 上限) +
   UiEllipsize(300 字符名字只比单字符宽 44px,旧实现要多 5100px)。
2. 统计信息两列等宽(新增 EdgeRisingText/EdgeFallingText/EdgeCountsPending):面板宽度按
   2×max(两列文本宽)+kEdgePanelStatColumnGap 预留,绘制时两列各占 (可用宽−间距)/2 ⇒ 多条信号的
   “上升沿/下降沿”列起点固定、严格垂直对齐。
3. 未完成时 rising 与 falling 两个数字后面都带 `…`(旧实现只在整行末尾追加一次,
   视觉上只跟在“下降沿”的数字后面)。

四、弱峰调整面板
1. 每行滑块右侧新增复位控件(复用还原图标绘制 PaintPanelResetIcon):悬停高亮 + IDC_HAND +
   一次性提示;点击恢复幅度 0 / 迭代 1 且 maxIterations 回到初始上限 10。
   EdgePanelLayout 新增 rowLevelResets/rowIterResets(并进 PanelSlotKey),两行按钮共用同一 X。
2. 迭代次数上限输入框的空内容提示改为“输入上限(当前N)”。
3. 滑块标签去掉多余的 %/N(kWeakPeakLevelLabel=调整幅度;新增 kWeakPeakIterationsSliderLabel=
   迭代次数;MEAN / CENTER 输入框的“迭代次数 N”保持不变)。

五、验证(全部离屏;进程/线程 BELOW_NORMAL、窗口 SW_HIDE、不注入真实鼠标键盘)
- 新增 .preflight/perf/panelcheck_main.cpp:46/46 通过(符号规则走真实菜单路径;按帧推进 /
  不饿死 / 精确性 / 自持驱动;两列等宽预留(含未完成时两处 …);名字上限;
  弱峰复位控件位置 / 对齐 / 点击恢复默认值与上限)。
- 既有 verify_main.cpp:47/47(另 2 条为本机 .preflight 未随远端同步的旧规则断言)。
- make release 零警告。

规则同步:Documents/agent_mem.md 新增 §9.26,§11.2 新增第 43–48 条硬约束,变更日志补一行。
This commit is contained in:
DeepSeek V4.1 Flash committed 2026-10-15 01:35:47 +08:00
1 parent 65f7625dce
commit fba40ee1a2
4 files changed
+564 -80

No files matched your search

+61 -15
View File
@@ -193,24 +193,65 @@ namespace jdi
}
/**
* @brief 带正负符号的数值文本:正数也带 `+`(非编辑态显示偏移量时使用)。
* @brief 带正负符号的数值文本(非编辑态显示偏移量时使用)。
*
* 规则(用户明确要求):**正数带 `+`、负数带 `-`、0 不带任何符号**。
* (旧实现 0 也会显示 `+0.00`,用户报告“偏移调整的 Y 轴输入框默认值为 0 时也显示了正号,这不正确”。)
* @param [IN] value 数值。
* @return 形如 `+1.50` / `-0.25` / `+0.00` 的文本。
* @return 形如 `+1.50` / `-0.25` / `0.00` 的文本。
*/
std::wstring FormatSignedNumber(double value)
{
const std::wstring text = FormatNumberText(std::fabs(value));
return ((value < 0.0) ? L"-" : L"+") + text;
if (value > 0.0)
{
return L"+" + text;
}
if (value < 0.0)
{
return L"-" + text;
}
return text; // 0:不带符号
}
/**
* @brief 把系数格式化为界面文本(与偏移量同一套两位小数规则)。
* @brief 把系数格式化为界面文本(与偏移量同一套两位小数规则 + 显式正号)。
*
* 规则(用户明确要求):“系数调整”输入框**非编辑态**显示时,非 0 正值前面加 `+`,
* 负值按原样显示负号,0 不带符号。编辑态仍用不带符号的纯数值(见 `FormatNumberText`)。
* @param [IN] gain 系数。
* @return 显示文本。
*/
std::wstring FormatGainText(float gain)
{
return FormatNumberText(static_cast<double>(gain));
const double value = static_cast<double>(gain);
if (value > 0.0)
{
return L"+" + FormatNumberText(value);
}
return FormatNumberText(value);
}
/**
* @brief 「时间轴」偏移的非编辑态显示文本(`Δt:` + 带正负号的复合单位间隔)。
*
* 规则(用户明确要求):在 `Δt:` 冒号后的时间显示值**前面加上正负号**,
* 只有值为 0 时不显示符号。示例:`Δt:+1h 1min 2s` / `Δt:-5s 60ms` / `Δt:0s`。
*(`FormatDurationCompound` 内部取绝对值,因此符号由这里补上。)
* @param [IN] seconds 时间偏移(秒,可为负)。
* @return 显示文本。
*/
std::wstring FormatOffsetTimeText(double seconds)
{
if (seconds > 0.0)
{
return L"Δt:+" + FormatDurationCompound(seconds);
}
if (seconds < 0.0)
{
return L"Δt:-" + FormatDurationCompound(seconds);
}
return L"Δt:" + FormatDurationCompound(0.0);
}
}
@@ -2012,13 +2053,15 @@ namespace jdi
gainHeader.id = CMD_GAIN_RESET; // 点击标题栏右侧的还原图标 → 系数重置为 1
items.push_back(gainHeader);
// 非编辑态带正号(用户明确要求);进入编辑后的初始文本用不带符号的纯数值。
const std::wstring gainText = FormatGainText(style->gain);
const std::wstring gainEditText = FormatNumberText(static_cast<double>(style->gain));
MenuItem gainEdit;
gainEdit.kind = MenuItemKind::EDIT;
gainEdit.label.clear(); // 不显示说明文字:输入框独占整行
gainEdit.id = CMD_GAIN_EDIT;
gainEdit.value = gainText;
gainEdit.editValue = gainText;
gainEdit.editValue = gainEditText;
gainEdit.filter = EditFilter::NUMBER;
items.push_back(gainEdit);
@@ -2039,13 +2082,13 @@ namespace jdi
items.push_back(offsetY);
// 「时间轴」:把采样沿时间轴平移(画在 t + offsetTime)。
// 非编辑态显示与双光标差值同一套复合单位文本;编辑态显示以秒为单位的数值,
// 并在输入框最右侧提示单位 `s`。
// 非编辑态显示与双光标差值同一套复合单位文本,并**带正负号**(用户明确要求);
// 编辑态显示以秒为单位的数值,并在输入框最右侧提示单位 `s`。
MenuItem offsetTime;
offsetTime.kind = MenuItemKind::EDIT;
offsetTime.label = L"时间轴";
offsetTime.id = CMD_OFFSET_TIME;
offsetTime.value = L"Δt:" + FormatDurationCompound(style->offsetTime);
offsetTime.value = FormatOffsetTimeText(style->offsetTime);
offsetTime.editValue = FormatNumberText(style->offsetTime);
offsetTime.editSuffix = L"s";
offsetTime.filter = EditFilter::NUMBER;
@@ -2907,7 +2950,8 @@ namespace jdi
}
if (m_menu != nullptr)
{
m_menu->UpdateItemValue(CMD_GAIN_EDIT, FormatGainText(style->gain), FormatGainText(style->gain));
m_menu->UpdateItemValue(CMD_GAIN_EDIT, FormatGainText(style->gain),
FormatNumberText(static_cast<double>(style->gain)));
}
return false;
}
@@ -2936,7 +2980,7 @@ namespace jdi
if (m_menu != nullptr)
{
m_menu->UpdateItemValue(CMD_OFFSET_TIME,
L"Δt:" + FormatDurationCompound(style->offsetTime),
FormatOffsetTimeText(style->offsetTime),
FormatNumberText(style->offsetTime));
}
return false;
@@ -2959,7 +3003,8 @@ namespace jdi
style->gain = 1.0f;
if (m_menu != nullptr)
{
m_menu->UpdateItemValue(CMD_GAIN_EDIT, FormatGainText(style->gain), FormatGainText(style->gain));
m_menu->UpdateItemValue(CMD_GAIN_EDIT, FormatGainText(style->gain),
FormatNumberText(static_cast<double>(style->gain)));
}
}
return false;
@@ -2974,9 +3019,10 @@ namespace jdi
style->offsetTime = 0.0;
if (m_menu != nullptr)
{
m_menu->UpdateItemValue(CMD_OFFSET_Y, FormatSignedNumber(0.0), L"0.00");
m_menu->UpdateItemValue(CMD_OFFSET_TIME, L"Δt:" + FormatDurationCompound(0.0),
L"0.00");
m_menu->UpdateItemValue(CMD_OFFSET_Y, FormatSignedNumber(0.0),
FormatNumberText(0.0));
m_menu->UpdateItemValue(CMD_OFFSET_TIME, FormatOffsetTimeText(0.0),
FormatNumberText(0.0));
}
}
return false;