自适应范围/时机澄清 + 适应视图与 Y 轴自适应互不干扰 + 吸附跟随时间偏移

一、澄清「自适应范围与时机」(并修正记忆里不完善的表述)
 * 准确说法:「额外要求视界内至少有一个可见数据点」这句不完善,完整规则是:
   - 首个网格显示内容时对它做完整自适应 —— **Y 轴与时间轴都**匹配自己的数据范围;
   - 只有一个数据点的信号必须显示在网格**中间**,保证其可见
     (FitSinglePointSignal:数据点居中、时间跨度 1s、Y 轴跨度 1);
   - **后续新增网格只做 Y 轴自适应,不再调整时间轴**。
 * Documents/agent_mem.md 相应章节(§9.30 正文、§11.2 第 58/60 条)已按此改写。

二、修复「适应视图」与「Y 轴自适应」互相干扰
 * 根因:两条自适应路径的留白不同 —— 整段自适应(ComputeLaneRange → PadRange)用 6%,
   「选区 Y 轴自适应」(AutoRangeLaneToView)用 5%。视界未变时两者本应完全相等,
   于是「点过适应视图 → 打开 Y 轴自适应」会算出不同区间、网格被无意义重绘
   (实测切一次 143513 像素),反向操作同样(137326 像素)。
 * 修法:新增 DataModel.hpp 的 PadValueRange() 作为**唯一**留白实现(含退化区间规则
   max(1, |值|×0.1)),PadRange 与 AutoRangeLaneToView 共用它。
 * 结果:「适应视图 → 开自适应 → 关自适应 → 再适应视图」四个步骤上生效区间逐位相同,
   绘图区逐像素 0 差异;而真正改变视界(缩放)时仍然正常重绘。

三、修复「适应视图」在单点信号上让数据点“飞了”
 * FitAll() 开头先尝试 FitSinglePointSignal():仅一个网格、且唯一可见信号只有一个有效
   数据点时,走与首次加载完全相同的单点初始视图(数据点居中、时间跨度 1s、Y 跨度 1),
   并保持幂等(重复点击像素不变)。

四、修复光标吸附未考虑「调整 → 时间轴偏移」
 * 根因:采样点 k 画在 time[k] + offsetTime 处,而光标存的也是显示时间,
   但吸附一直在源时间空间查找 —— 设过时间轴偏移后吸附会落到历史位置。
 * 修法:SignalSnapEnabled / HasVisibleSample / NearestSnapSample / LocalSampleSpacing
   全部增加 timeShift 形参:可见性与点数统计比较 time[k] + timeShift;
   NearestSnapSample 内部用 time - timeShift 回源空间查找、返回时再加回去;
   UpdateAutoScatterStyles(自动点线)同步传入 offsetTime,保持与吸附判定同源。

五、验证(离屏 .preflight/check:SW_HIDE 窗口、不注入真实鼠标键盘、进程/线程 BELOW_NORMAL)
 39/39 通过(连跑 3 次稳定):
 [A] 四个步骤的生效区间逐位相同 + 绘图区逐像素 0 差异(对照组:真缩放重绘 79581 像素);
 [B] 单点信号的适应视图与首次加载一致(1s/1、居中、幂等);
 [C] 首个网格自适应时间轴、后续新增/隐藏信号都不再改动时间轴;
 [D] 偏移 +0.3 时吸到“画面上的点”,偏移 0 行为不变,偏移把点移出视界时不吸附;
 [E] 回归:单点默认样式(点线 + 最大线宽)与“2…16 点不再改点线”均保持。
 A/B:回退三项修复 ⇒ 7 项如实失败(含 143513 / 137326 像素的重绘症状)。
 make release 零警告(用户正运行 Target.exe,链接用临时 -o 名字验证通过)。

六、规则同步
 Documents/agent_mem.md 新增 §9.31、§11.2 第 58–63 条(含修正后的自适应范围/时机规则)
 与变更日志。
This commit is contained in:
DeepSeek V4.1 Flash committed 2026-10-15 15:46:14 +08:00
1 parent 57cc714c1d
commit 68d8b7ede6
5 files changed
+202 -80

No files matched your search

+17 -7
View File
@@ -344,22 +344,26 @@ namespace jdi
*
* ⚠️ 额外要求视界内**至少有一个可见数据点**:数据点全在视界之外的信号既没有可吸附
* 的对象、也没有可提示的内容(否则它会被判成“0 < N×4 稀疏”而白切一次「点线」)。
* ⚠️ 可见性一律按**显示时间**(`采样时刻 + timeShift`)判断 —— `timeShift` 就是该信号的
* `SignalStyle::offsetTime`(存在时间轴偏移时不能拿源时刻去比视界)。
*
* @param [IN] axis 该信号使用的时间轴。
* @param [IN] signal 目标信号。
* @param [IN] timeShift 该信号的显示时间偏移(`offsetTime`)。
* @return 开启吸附返回 true。
*/
bool SignalSnapEnabled(const TimeAxis& axis, const SignalData& signal) const;
bool SignalSnapEnabled(const TimeAxis& axis, const SignalData& signal, double timeShift) const;
/**
* @brief 判断某信号在给定可见下标区间内是否存在**落在视界内**的有效采样点。
* @param [IN] axis 该信号使用的时间轴。
* @param [IN] signal 目标信号。
* @param [IN] begin 可见下标区间起点。 @param [IN] end 终点(不含)。
* @param [IN] timeShift 该信号的显示时间偏移(`offsetTime`)。
* @return 存在返回 true。
*/
bool HasVisibleSample(const TimeAxis& axis, const SignalData& signal, std::size_t begin,
std::size_t end) const;
std::size_t end, double timeShift) const;
/**
* @brief 找某信号**可见区间内**离给定时刻最近的有效采样点。
@@ -367,24 +371,30 @@ namespace jdi
* 稀疏信号(有效点已缓存)用二分;一般信号从最近下标向两侧**有界**探测
*(NaN 只出现在缺少数据的区段)。视界之外的采样点不是合法落点,一律排除。
*
* ⚠️ `time` 与输出的 `candidate` 都是**显示时间**(画面上的时刻):内部按 `timeShift`
* 换算回源空间查找、再把结果加回去,因此存在「调整 → 时间轴偏移」时也能吸到
* **画面上**的数据点,而不是历史位置。
*
* @param [IN] axis 该信号使用的时间轴。
* @param [IN] signal 目标信号。
* @param [IN] time 目标时刻。
* @param [OUT] candidate 最近的可见有效采样点时刻。
* @param [IN] time 目标时刻(显示时间)。
* @param [IN] timeShift 该信号的显示时间偏移(`offsetTime`)。
* @param [OUT] candidate 最近的可见有效采样点时刻(显示时间)。
* @return 找到返回 true。
*/
bool NearestSnapSample(const TimeAxis& axis, const SignalData& signal, double time,
double& candidate) const;
double timeShift, double& candidate) const;
/**
* @brief 取某时刻附近的**局部采样间距**(时间单位,用于限制吸附半径)。
* @param [IN] axis 该信号使用的时间轴。
* @param [IN] signal 目标信号。
* @param [IN] time 目标时刻。
* @param [IN] time 目标时刻(显示时间)。
* @param [IN] timeShift 该信号的显示时间偏移(`offsetTime`)。
* @param [OUT] spacing 相邻采样点的时间间隔(无法判定时为 0)。
*/
void LocalSampleSpacing(const TimeAxis& axis, const SignalData& signal, double time,
double& spacing) const;
double timeShift, double& spacing) const;
/**
* @brief 把时间吸附到距今最近的数据采样点。