组名平滑悬浮 + 单点信号初始视图 + 光标吸附统一判定(自动点线同源)
一、信号列表「组名悬浮」平滑化、且不再遮住下一个组名
* 悬浮位置改为 min(列表上边界, 本组最后一行下边界 − 组名高度):
- 本组还有行在列表里时钉在列表上边界;
- 本组最后一行开始滑出上边界时,组名跟着一起上移、随滚动条自然离界
(不再像旧实现那样突然消失);
- 「本组最后一行的下边界」恰好就是下一个分组行的自然位置,因此悬浮组名的下边界
永远不超过下一组名 —— 旧实现固定钉在列表上边界会把下一组名盖住,让别人以为
后面的信号属于上一组。
* 折叠的分组不再悬浮(折叠时本组“最后一行”就是它自己,悬浮位置 = 自然位置,
于是随滚动自然划过,不会停在最上方)。
* 底部边界线只在真的“浮”在其它行之上时才画,避免与正常绘制出现像素差。
二、单点信号的初始视图
* 新增 PlotView::FitSinglePointSignal():当前没有任何信号被勾选、而勾选的第一个信号
只有一个有效数据点时,把该数据点居中放置,时间跨度 = 1s、Y 轴跨度 = 1
(各为一个大格),并关闭该网格的整段自适应以免被覆盖。
Application::SynchronizeView 在首次勾选时先尝试它,失败才走 FitAll()。
* AutoRangeLaneToView 的退化区间(可见区间内只有一个取值)改用与 PadRange 一致的留白
max(1, |v|×0.1),不再产生“几乎为 0”的区间(那会让幅值网格线挤成一团、看不出数据点)。
* 需求作废:上一轮“2…16 个有效采样点默认改为点线 + 2px”已取消(放大自动点线已覆盖该场景);
只保留“只有一个有效采样点 → 点线 + 最大线宽”。
* SignalData::singlePointTime 升级为 sparseTimes/sparseValues(有效点 ≤ 1024 时在加载/插入
时缓存全部有效点),同时服务单点初始视图与光标吸附。
三、光标吸附的统一判定
* N = GridMinorCellCount():网格底衬一横排的小格数,与网格绘制用同一套
NiceStep + 五等分算出(因此不随缩放等级大幅变化)。
* SignalSnapEnabled():当且仅当某信号在当前一屏内的数据点个数 < N×4 时为它开启吸附;
判据随视界实时重算 ⇒ 疏密变化的信号在稀疏区自动吸附、移回稠密区自动自由移动。
额外要求视界内至少有一个可见数据点(否则“0 < N×4”会把数据全在视界之外的信号
误判为稀疏)。统计只在可见下标区间内进行且攒够阈值即早退,代价有界。
* SnapTimeToSample():判定只针对鼠标所在网格(同一个时间轴上不同网格结果可以不同);
同一网格内所有符合判定的信号的数据点都参与吸附比较;
吸附半径 = 一个网格小格的宽度,且不超过半个局部采样间距 ⇒ 数据点很稀疏
(一屏只有一两个点)时光标在点之间仍可自由移动(吸附是提示,不是卡住)。
* 删除旧的 SampleSpacingPixels(6px 下门限 + 半屏宽上门限)。
四、放大自动切点线改为与吸附判定完全同源
* UpdateAutoScatterStyles() 改用 SignalSnapEnabled:进入判定自动切「点线」,
不再满足(适应视图、或视界从稀疏区移到稠密区)自动恢复原样式;
自动期间用户手动改过则不恢复(尊重用户选择,判定放在恢复之前)。
* 删除旧的 AxisSparseInView(“采样间距 > 半屏宽”)。
五、验证(离屏 .preflight/check:SW_HIDE 窗口、不注入真实鼠标键盘、进程/线程 BELOW_NORMAL)
55/55 通过(连跑 3 次稳定):[A] 组名悬浮(钉住/平滑上移/整组滚过后换下一组/折叠组不悬浮/
悬浮下边界不超过下一组名)、[B] 单点初始视图(跨度 1s 与 1、居中、点线 + 最大线宽、
两信号时不再走单点路径)、[C] 吸附判定(满视图都关闭/稀疏窗口开启/无可见点不开启/
同一时间轴不同网格结果不同/同网格多信号的点都参与/稠密↔稀疏双向动态切换/
单点靠近吸附远离自由)、[D] 自动点线(同源判定/恢复/用户手动改过不覆盖)。
A/B:临时禁用「悬浮位置公式 / 单点初始视图 / 吸附判定」后重跑 ⇒ 14 项如实失败。
make release 零警告(用户正运行 Target.exe,链接用临时 -o 名字验证通过)。
六、规则同步
Documents/agent_mem.md 新增 §9.30、§11.2 第 58–61 条硬约束与变更日志。
This commit is contained in:
1 parent
9f6de3bba4
commit
57cc714c1d
8 files changed
+808
-230
No files matched your search
@@ -310,11 +310,12 @@ namespace jdi
|
||||
void ReapplyGlobalAutoFitIfEnabled();
|
||||
|
||||
/**
|
||||
* @brief 依据「采样点是否稀疏」自动启用/恢复「点线」绘制方式。
|
||||
* @brief 依据「光标吸附判定」自动启用/恢复「点线」绘制方式。
|
||||
*
|
||||
* 用户要求(2026-10-15):把波形放大到一定程度(此时光标会吸附数据点)后,
|
||||
* 线段模式已经看不出数据点在哪 → 自动为该信号启用「点线」提示;视界回退到阈值
|
||||
* 之外(例如适应视图)时自动恢复**原来的**绘制方式。
|
||||
* 用户要求(2026-10-15):**仅在满足光标吸附判定的信号上**生效 ——
|
||||
* 即“一屏内数据点少于 N×4”(见 `SignalSnapEnabled`)时自动切到「点线」提示
|
||||
* 用户数据点的位置;一旦不再满足(适应视图、或把视界从稀疏区移到稠密区)
|
||||
* 自动恢复**原来的**绘制方式。
|
||||
*
|
||||
* ⚠️ 自动启用期间若用户**手动**改了绘制方式(`SignalStyle::autoScatterUserOverride`
|
||||
* 由本函数自动识别:自动状态下 mode 不等于 `LINE_SCATTER` 即说明是用户改的),
|
||||
@@ -325,24 +326,76 @@ namespace jdi
|
||||
bool UpdateAutoScatterStyles();
|
||||
|
||||
/**
|
||||
* @brief 判断某个时间轴在当前视界下是否已经“稀疏到需要点线提示”。
|
||||
* @param [IN] axis 目标时间轴。
|
||||
* @return 采样点间距超过半个绘图区宽度返回 true。
|
||||
* @brief 网格底衬**一横排的小格数量 N**(与 `PaintLane` 画出来的网格同源)。
|
||||
*
|
||||
* 时间刻度用 `NiceStep(span, kTimeTickTarget)` 定主刻度、再五等分 ⇒
|
||||
* 不管缩放等级如何,一横排总是差不多同样多的小格。
|
||||
*
|
||||
* @return 小格数量(无法判定时返回 0)。
|
||||
*/
|
||||
bool AxisSparseInView(const TimeAxis& axis) const;
|
||||
int GridMinorCellCount() const;
|
||||
|
||||
/**
|
||||
* @brief 判断某信号在当前视界下是否**开启光标吸附**。
|
||||
*
|
||||
* 用户 2026-10-15 的定义:**当且仅当**该信号在“当前一屏”(当前视界)内的数据点
|
||||
* 个数 `< N × 4`(N = `GridMinorCellCount()`)时开启吸附。
|
||||
* 判据随视界实时重算 ⇒ 疏密变化的信号在稀疏区自动吸附、稠密区自动自由移动。
|
||||
*
|
||||
* ⚠️ 额外要求视界内**至少有一个可见数据点**:数据点全在视界之外的信号既没有可吸附
|
||||
* 的对象、也没有可提示的内容(否则它会被判成“0 < N×4 稀疏”而白切一次「点线」)。
|
||||
*
|
||||
* @param [IN] axis 该信号使用的时间轴。
|
||||
* @param [IN] signal 目标信号。
|
||||
* @return 开启吸附返回 true。
|
||||
*/
|
||||
bool SignalSnapEnabled(const TimeAxis& axis, const SignalData& signal) const;
|
||||
|
||||
/**
|
||||
* @brief 判断某信号在给定可见下标区间内是否存在**落在视界内**的有效采样点。
|
||||
* @param [IN] axis 该信号使用的时间轴。
|
||||
* @param [IN] signal 目标信号。
|
||||
* @param [IN] begin 可见下标区间起点。 @param [IN] end 终点(不含)。
|
||||
* @return 存在返回 true。
|
||||
*/
|
||||
bool HasVisibleSample(const TimeAxis& axis, const SignalData& signal, std::size_t begin,
|
||||
std::size_t end) const;
|
||||
|
||||
/**
|
||||
* @brief 找某信号**可见区间内**离给定时刻最近的有效采样点。
|
||||
*
|
||||
* 稀疏信号(有效点已缓存)用二分;一般信号从最近下标向两侧**有界**探测
|
||||
*(NaN 只出现在缺少数据的区段)。视界之外的采样点不是合法落点,一律排除。
|
||||
*
|
||||
* @param [IN] axis 该信号使用的时间轴。
|
||||
* @param [IN] signal 目标信号。
|
||||
* @param [IN] time 目标时刻。
|
||||
* @param [OUT] candidate 最近的可见有效采样点时刻。
|
||||
* @return 找到返回 true。
|
||||
*/
|
||||
bool NearestSnapSample(const TimeAxis& axis, const SignalData& signal, double time,
|
||||
double& candidate) const;
|
||||
|
||||
/**
|
||||
* @brief 取某时刻附近的**局部采样间距**(时间单位,用于限制吸附半径)。
|
||||
* @param [IN] axis 该信号使用的时间轴。
|
||||
* @param [IN] signal 目标信号。
|
||||
* @param [IN] time 目标时刻。
|
||||
* @param [OUT] spacing 相邻采样点的时间间隔(无法判定时为 0)。
|
||||
*/
|
||||
void LocalSampleSpacing(const TimeAxis& axis, const SignalData& signal, double time,
|
||||
double& spacing) const;
|
||||
|
||||
/**
|
||||
* @brief 把时间吸附到距今最近的数据采样点。
|
||||
*
|
||||
* 仅在横向放大到采样点间距足够大(可精确分辨)时生效,
|
||||
* 避免缩小时光标出现“抖动”难以微调。
|
||||
*
|
||||
* ⚠️ 吸附有**上下两个门限**:
|
||||
* * 下门限:采样点间距 ≥ `kCursorSnapMinPixels`(缩小时不吸附,否则难以微调);
|
||||
* * 上门限:视界内至少能容纳 **2 个**采样点(`pixels <= plotWidth / 2`)。
|
||||
* 视界比采样间隔还窄时“最近采样点”在视界之外,吸附会把光标一把甩到视界边缘
|
||||
* 并钉住(表现为“一拖光标就飞到视界边缘、跟不动鼠标”)。
|
||||
* * 并且只接受**落在当前可见区间内**的采样点。
|
||||
* 判定与半径(用户 2026-10-15 统一后的定义):
|
||||
* * **判定只针对鼠标所在的网格**:该网格内的信号各自按 `SignalSnapEnabled` 判定,
|
||||
* 符合条件的信号的数据点全部参与吸附;网格内没有符合条件的信号时完全自由移动;
|
||||
* * 同一网格内有多条信号时,所有符合条件的数据点都参与比较;
|
||||
* * 吸附半径 = **一个网格小格的宽度**(与判定同源),且**不超过半个局部采样间距**
|
||||
* ⇒ 数据点很稀疏(一屏只有一两个点)时光标在点与点之间仍可自由移动,
|
||||
* 而不是被“死死吸住”(吸附只是提示性响应,不是卡住光标)。
|
||||
*
|
||||
* @param [IN] time 原始时间。
|
||||
* @param [OUT] snapped 吸附后的时间。
|
||||
@@ -350,15 +403,6 @@ namespace jdi
|
||||
*/
|
||||
bool SnapTimeToSample(double time, double& snapped) const;
|
||||
|
||||
/**
|
||||
* @brief 计算指定文件中某一时刻附近采样点的时间间隔。
|
||||
* @param [IN] file 目标文件。
|
||||
* @param [IN] time 目标时刻。
|
||||
* @param [OUT] pixels 间隔换算到屏幕上的像素宽度。
|
||||
* @return 计算成功返回 true。
|
||||
*/
|
||||
bool SampleSpacingPixels(const TimeAxis& axis, double time, double& pixels) const;
|
||||
|
||||
/**
|
||||
* @brief 判断坐标是否落在绘图区范围内。
|
||||
* @param [IN] x 客户区横坐标。
|
||||
@@ -380,6 +424,19 @@ namespace jdi
|
||||
*/
|
||||
void FitAll();
|
||||
|
||||
/**
|
||||
* @brief 「单点信号」的初始视图:把唯一的数据点居中,时间跨度 1s、幅值跨度 1。
|
||||
*
|
||||
* 用户要求(2026-10-15):当前**没有任何信号被勾选**、而勾选的第一个信号
|
||||
* **只有一个有效数据点**时,整段自适应并不能把它的状态显示出来
|
||||
*(时间轴会覆盖整条时间列、幅值区间退化成几乎为 0)。
|
||||
* 因此这种情况改为:数据点居中放置,**时间轴跨度 = 一个大格 = 1s**、
|
||||
* **Y 轴跨度 = 一个大格 = 1**。
|
||||
*
|
||||
* @return 已按“单点信号”初始化返回 true(调用方无需再做 `FitAll`)。
|
||||
*/
|
||||
bool FitSinglePointSignal();
|
||||
|
||||
/**
|
||||
* @brief 以给定倍率缩放时间轴(以视图中心为锚点)。
|
||||
* @param [IN] factor 缩放倍率,大于 1 表示放大。
|
||||
|
||||
Reference in new issue
Block a user