全局 Y 轴自适应跟随网格结构 + 组名悬浮 + 稀疏信号默认点线 + 单点光标吸附 + 放大自动点线
一、全局「Y 轴自适应」跟随网格结构
* 新勾选信号产生的新网格现在会**继承**该开关:RebuildLanes 重建前先算
previousAllAutoFit(非空且每个网格都开着 = 全局开启的正式定义),LaneCarry 加
matched 标记,新出现的信号用它作为 fitYToSelection 的默认值。
* 新增 PlotView::ReapplyGlobalAutoFitIfEnabled(),在 Layout()(网格数量变化 →
Relayout)、RefreshLanes()(信号数量变化)、SplitLane() 之后立即重算一次。
⇒ 新勾选(按当前视界自适应)、多信号网格内取消显示某信号(剩余信号重新收敛)、
拆分网格(每份各自自适应)三项都覆盖。
二、信号列表「组名悬浮」
* 分组行被滚出列表上边界、而该组仍有信号留在列表里时,把分组行钉在列表顶部
(不透明底 + 底部边界线);整组翻过去后本组组名消失,改由下一组悬浮。
* 新增 SignalPanel::StickyGroupRowIndex() / PaintStickyGroupRow();RowRect 增加
m_rowYOverrideActive/m_rowYOverrideY 覆盖行顶部坐标(RowRect 是行内名称框 / ✕ /
徽标的唯一几何来源)。常规行循环跳过被钉住的那一行,避免同一行被画两次。
三、「少数数据点」信号的默认样式
* 有效采样点 1 个 → 「点线」+ 最大线宽(10px);2 … 16 个 → 「点线」+ 2px;
其余保持「线段」+ 1px。只在新建信号时生效(BuildSignalGroup 与
InsertDerivedSignal),不覆盖用户设置。
* 线宽范围常量统一到 DataModel.hpp(kSignalLineWidthMin/Max),SignalPanel 的线宽
滑块改为引用它们(原本两处各自硬编码,容易漂移)。
四、单点信号的光标吸附(吸附而非卡住)
* 旧逻辑要求时间轴至少 2 个采样点才能算「采样间距」,单点信号因此永远不吸附。
* 新增 SignalData::singlePointTime(加载时与 ComputeStats 同趟扫描缓存该点时刻),
SnapTimeToSample 新增半径分支(kCursorSnapPointRadiusPixels = 8 逻辑像素):
半径内吸附到该点,半径外自由移动(移开即自由,不是把光标吸住)。
五、放大到「采样点稀疏」时自动启用点线
* SignalStyle 新增运行态三字段:autoScatterActive / autoScatterSavedMode /
autoScatterUserOverride。
* AxisSparseInView()(采样点间距 > 半个绘图区宽度,与光标吸附上门限同源;单点时间轴
不算稀疏)+ UpdateAutoScatterStyles():进入阈值保存原绘制方式并切「点线」,
退出阈值(如适应视图)恢复;平移不改变缩放等级 ⇒ 结果稳定,不会样式抖动。
* 自动启用期间用户手动改了绘制方式则退出时**不恢复**(尊重用户选择);该判定必须在
恢复判断之前完成 —— 用户改完样式可能紧接着就退出阈值,否则会被覆盖。
* 钩子:SetTimeView(视界唯一写入口)与 Layout / RefreshLanes / SplitLane。
六、验证(离屏 .preflight/check:SW_HIDE 窗口、不注入真实鼠标键盘、进程/线程 BELOW_NORMAL)
65/65 通过(连跑 3 次稳定):[A1]/[A2]/[A3] 全局自适应、[B] 组名悬浮(含钉住行与自然
位置逐像素一致、整组翻过后组名消失)、[C] 稀疏默认样式边界(1/2/5/16/17/1000 点)、
[D] 单点吸附(半径内吸附、半径外自由、无残留粘滞)、[E] 自动点线(进入/退出/平移稳定/
用户手动改过后不覆盖/再次进入再启用)。
A/B:临时禁用六个机制后重跑 ⇒ 21 项如实失败(覆盖全部六项),证明断言有判别力。
make release 零警告(用户正运行 Target.exe,链接用临时 -o 名字验证通过)。
七、规则同步
Documents/agent_mem.md 新增 §9.29、§11.2 第 55–58 条硬约束与变更日志。
This commit is contained in:
1 parent
3bca3b1201
commit
9f6de3bba4
7 files changed
+559
-5
No files matched your search
@@ -64,6 +64,7 @@ namespace jdi
|
||||
// 随网格携带的“网格自身状态”(按信号继承):重建网格时不能丢。
|
||||
struct LaneCarry
|
||||
{
|
||||
bool matched = false; /**< 是否找到该信号原本所在的网格(false = 新出现的信号)。 */
|
||||
bool fitYToSelection = false;
|
||||
bool edgeCount = false;
|
||||
double edgeValue = 0.0;
|
||||
@@ -90,10 +91,25 @@ namespace jdi
|
||||
|
||||
// 先记下每个信号原来所在网格的网格自身状态:
|
||||
// 勾选可见性会重建网格,若不携带,用户刚打开的开关会被悄悄清掉。
|
||||
//
|
||||
// ⚠️ 同时记下「重建之前是否**每个**网格都开着 `fitYToSelection`」:
|
||||
// 那就是「全局 Y 轴自适应」的正式定义。新勾选信号产生的**新网格**必须继承它,
|
||||
// 否则全局开关看着是开的、新网格却不按当前视界自适应(用户报告的缺陷)。
|
||||
bool previousAllAutoFit = !lanes.empty();
|
||||
for (const Lane& lane : lanes)
|
||||
{
|
||||
if (!lane.fitYToSelection)
|
||||
{
|
||||
previousAllAutoFit = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::pair<SignalRef, LaneCarry>> previousCarry;
|
||||
for (const Lane& lane : lanes)
|
||||
{
|
||||
LaneCarry carry;
|
||||
carry.matched = true;
|
||||
carry.fitYToSelection = lane.fitYToSelection;
|
||||
carry.edgeCount = lane.edgeCount;
|
||||
carry.edgeValue = lane.edgeValue;
|
||||
@@ -132,10 +148,11 @@ namespace jdi
|
||||
}
|
||||
return LaneCarry();
|
||||
};
|
||||
const auto applyCarry = [&inheritedCarry](Lane& lane, const SignalRef& ref)
|
||||
const auto applyCarry = [&inheritedCarry, previousAllAutoFit](Lane& lane, const SignalRef& ref)
|
||||
{
|
||||
const LaneCarry carry = inheritedCarry(ref);
|
||||
lane.fitYToSelection = carry.fitYToSelection;
|
||||
// 新出现的信号(之前没有网格):继承「全局 Y 轴自适应」是否开启。
|
||||
lane.fitYToSelection = carry.matched ? carry.fitYToSelection : previousAllAutoFit;
|
||||
lane.edgeCount = carry.edgeCount;
|
||||
lane.edgeValue = carry.edgeValue;
|
||||
lane.edgeValueValid = carry.edgeValueValid;
|
||||
@@ -353,8 +370,37 @@ namespace jdi
|
||||
double minValue = 0.0; /**< 有限采样值最小值。 */
|
||||
double maxValue = 0.0; /**< 有限采样值最大值。 */
|
||||
std::size_t finiteCount = 0; /**< 有限采样点数量。 */
|
||||
std::size_t firstFiniteIndex = 0; /**< 第一个有限采样点的下标(无有效值时无意义)。 */
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 按有效采样点数量设置「稀疏信号」的**初始**绘制样式。
|
||||
*
|
||||
* 有效采样点极少时线段模式几乎什么也看不见(只有一个点时更是完全画不出东西),
|
||||
* 因此这类信号初始就用「点线」并适当加粗:
|
||||
* * **只有一个有效采样点** → 点线 + `kSinglePointSignalLineWidth`(最大线宽):那个点本身
|
||||
* 就是一个圆点,半径与线宽成正比,不取最大值根本看不清;
|
||||
* * **有效采样点 2 … `kSparseSignalSampleLimit`** → 点线 + `kSparseSignalLineWidth`(2px)。
|
||||
*
|
||||
* 其余信号保持既有默认(线段 + 1px)。只在**新建信号**时调用(用户改过样式后不再覆盖)。
|
||||
*
|
||||
* @param [IN] finiteCount 有效采样点数量。
|
||||
* @param [IN,OUT] style 待初始化的显示外观。
|
||||
*/
|
||||
void ApplySparseDefaultStyle(std::size_t finiteCount, SignalStyle& style)
|
||||
{
|
||||
if (finiteCount == 1)
|
||||
{
|
||||
style.mode = DrawMode::LINE_SCATTER;
|
||||
style.lineWidth = kSinglePointSignalLineWidth;
|
||||
}
|
||||
else if (finiteCount <= kSparseSignalSampleLimit)
|
||||
{
|
||||
style.mode = DrawMode::LINE_SCATTER;
|
||||
style.lineWidth = kSparseSignalLineWidth;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 统计一列采样的最小/最大值与有效点数量,并顺带构建粗粒度包络层。
|
||||
*
|
||||
@@ -389,6 +435,10 @@ namespace jdi
|
||||
const double value = values[index];
|
||||
if (std::isfinite(value))
|
||||
{
|
||||
if (stats.finiteCount == 0)
|
||||
{
|
||||
stats.firstFiniteIndex = index; // 供「单点信号」缓存其时刻
|
||||
}
|
||||
++stats.finiteCount;
|
||||
low = std::min(low, value);
|
||||
high = std::max(high, value);
|
||||
@@ -569,6 +619,15 @@ namespace jdi
|
||||
signal.minValue = stats.minValue;
|
||||
signal.maxValue = stats.maxValue;
|
||||
signal.finiteCount = stats.finiteCount;
|
||||
// 只含一个有效采样点时缓存它的时刻(光标吸附用)。
|
||||
if (stats.finiteCount == 1)
|
||||
{
|
||||
const TimeAxis& ownAxis = group.axes[std::min(axisIndex, group.axes.size() - 1)];
|
||||
if (stats.firstFiniteIndex < ownAxis.time.size())
|
||||
{
|
||||
signal.singlePointTime = ownAxis.time[stats.firstFiniteIndex];
|
||||
}
|
||||
}
|
||||
|
||||
// 显示外观随信号一起初始化:调色板按「全局信号序号」取色,
|
||||
// 使同一信号在任意标签页中都保持同一默认颜色。
|
||||
@@ -592,6 +651,8 @@ namespace jdi
|
||||
signal.style.opacity = 1.0f;
|
||||
signal.style.lineWidth = 1.0f;
|
||||
signal.style.mode = DrawMode::LINE;
|
||||
// 采样点极少的信号(含只有一个点)改用「点线」并加粗,否则几乎不可见。
|
||||
ApplySparseDefaultStyle(stats.finiteCount, signal.style);
|
||||
}
|
||||
|
||||
group.signals.push_back(std::move(signal));
|
||||
@@ -656,6 +717,15 @@ namespace jdi
|
||||
signal.minValue = stats.minValue;
|
||||
signal.maxValue = stats.maxValue;
|
||||
signal.finiteCount = stats.finiteCount;
|
||||
// 只含一个有效采样点时缓存它的时刻(光标吸附用);派生信号沿用源信号的时间轴。
|
||||
if (stats.finiteCount == 1 && !group.axes.empty())
|
||||
{
|
||||
const TimeAxis& ownAxis = group.AxisAt(signal.axisIndex);
|
||||
if (stats.firstFiniteIndex < ownAxis.time.size())
|
||||
{
|
||||
signal.singlePointTime = ownAxis.time[stats.firstFiniteIndex];
|
||||
}
|
||||
}
|
||||
|
||||
// 2. 显示外观按「全局信号序号」取调色板颜色(与普通新信号完全一致的规则)。
|
||||
const int insertPos = afterSignalIndex + 1;
|
||||
@@ -676,6 +746,9 @@ namespace jdi
|
||||
signal.style.color = palette[slot];
|
||||
signal.style.customColor = palette[(slot + 1) % count];
|
||||
}
|
||||
|
||||
// 采样点极少的派生信号同样改用「点线」并加粗(与普通加载路径同一规则)。
|
||||
ApplySparseDefaultStyle(stats.finiteCount, signal.style);
|
||||
}
|
||||
|
||||
group.signals.insert(group.signals.begin() + insertPos, std::move(signal));
|
||||
|
||||
Reference in new issue
Block a user