fix(clip): 选区截取面板五处缺陷(条目/空白/多信号截取/滚轮)

一、未启用双光标时不再显示信号条目
   EdgePanelLayoutFor 与绘制侧都只判 clipPanel、没判双光标是否启用,
   于是照常累加内容高度、生成行矩形并绘制条目。
   - 高度算式新增 clipReady = clipPanel && CursorMode()==DOUBLE,未就绪时内容只占一行(提示);
   - 行几何循环加 clipRowsVisible,未就绪时不生成任何行矩形 => 绘制自然为空;
   - footer 同步改为"未就绪 = 0"。

二、条目区与底部按钮之间的大段空白(未启用双光标时也有)
   两个叠加根因:
   1) 高度算式的 maxContent 有"至少 3 行"下限 => 只有 1 个信号也硬撑 3 行高;
      改为 min(上限, max(1 行, budget))。
   2) 几何侧 viewHeight 多减了一个 pad(高度算式只含一个 pad + bottomPad);
      统一为 height - titleHeight - pad - footerHeight。
   高度算式与内容区几何必须严格同源。

三、多个信号「原始信号截取」结果不对
   同组信号共享同一条时间轴,ClipSignalRange 每次都裁剪 axis.time
   => 截完第一条后其它信号时间基准被改短 => 采样与时间错位。
   - 新增 DataSet::ForkAxisForSignal:轴非独占时复制一份新轴并让信号指向它;
   - ClipSignalRange 内再加一道守卫:轴仍被共享时直接返回 false(不改任何数据);
   - Application::ClipSignalInPlace 先 fork 再裁剪。

四、有滚动条时滚轮滚不到底
   1) OnMouseWheel 的 current 取值链漏了 CLIP => 读到 meanFilterScroll(恒 0)而写入 clipScroll,
      补上 CLIP 分支;
   2) PanelSlotKey 里 clipScroll 被写成布尔(>0 ? 1 : 0)=> 滚到 1px 后键不再变化,
      改为 mixFloat(lane.clipScroll) 真实值。

删 .o 全量重编译:零 error、零 warning,产物 861,184 B。
按用户要求未占用鼠标/键盘/屏幕,未运行 GUI 探针。
This commit is contained in:
DeepSeek V4.1 Flash committed 2026-11-03 16:39:18 +08:00
1 parent 62220477c0
commit 21a9865365
7 files changed
+199 -12

No files matched your search

+17
View File
@@ -689,6 +689,23 @@ namespace jdi
*/
bool ClipSignalRange(int groupIndex, int signalIndex, double low, double high);
/**
* @brief 让某条信号**独占一条时间轴**(复制一条新轴并把信号指向它)。
*
* 同组的信号常常共享同一条时间轴(合并网格、同文件多通道等)。
* 破坏性截取若要裁剪时间轴,就必须先让这条信号独占自己的轴,
* 否则会把其它信号的时间基准一起改短、导致它们数据错位
* (用户 2026-11-03 报告:"多个信号同时截取时截取出来的信号并不对")。
*
* 若该信号的时间轴**已经**被它独占,本函数不做任何事并返回 true。
*
* @param [IN] groupIndex 分组下标。
* @param [IN] signalIndex 信号下标。
* @param [OUT] axisIndex 该信号(新)轴的下标。
* @return 成功返回 true。
*/
bool ForkAxisForSignal(int groupIndex, int signalIndex, std::size_t& axisIndex);
/**
* @brief 移除指定下标的分组,并立即释放其占用的全部采样内存。
* @param [IN] groupIndex 分组下标。
+70
View File
@@ -499,6 +499,50 @@ namespace jdi
return true;
}
bool DataSet::ForkAxisForSignal(int groupIndex, int signalIndex, std::size_t& axisIndex)
{
axisIndex = 0;
if (groupIndex < 0 || groupIndex >= static_cast<int>(m_groups.size()))
{
return false;
}
SignalGroup& group = m_groups[static_cast<std::size_t>(groupIndex)];
if (signalIndex < 0 || signalIndex >= static_cast<int>(group.signals.size()))
{
return false;
}
SignalData& signal = group.signals[static_cast<std::size_t>(signalIndex)];
if (signal.axisIndex >= group.axes.size())
{
return false;
}
// 已经独占 ⇒ 不需要复制(避免每次截取都新增一条轴)。
bool shared = false;
for (std::size_t index = 0; index < group.signals.size(); ++index)
{
if (index == static_cast<std::size_t>(signalIndex))
{
continue;
}
if (group.signals[index].axisIndex == signal.axisIndex)
{
shared = true;
break;
}
}
axisIndex = signal.axisIndex;
if (!shared)
{
return true;
}
// 复制一条新轴(时间数组是只读共享,复制一份即可),并把信号指向它。
TimeAxis copy = group.axes[signal.axisIndex];
group.axes.push_back(std::move(copy));
signal.axisIndex = group.axes.size() - 1;
axisIndex = signal.axisIndex;
return true;
}
bool DataSet::ClipSignalRange(int groupIndex, int signalIndex, double low, double high)
{
if (groupIndex < 0 || groupIndex >= static_cast<int>(m_groups.size()))
@@ -545,6 +589,32 @@ namespace jdi
signal.values.swap(trimmed);
signal.values.shrink_to_fit();
}
// ⚠️⚠️ **时间轴可能被同组的多条信号共享**(合并网格里常见):
// 只有在"本组没有任何**其它**信号使用同一条轴"时才可以裁剪它;
// 否则裁剪会把其它信号的时间基准一起改短 ⇒ 它们的数据错位
// (用户 2026-11-03 报告:"多个信号同时截取时截取出来的信号并不对")。
// 共享轴的情况由调用方先把该信号改成使用**自己的一条新轴**(见 `Application::ClipSignalInPlace`)。
bool axisShared = false;
for (std::size_t index = 0; index < group.signals.size(); ++index)
{
if (index == static_cast<std::size_t>(signalIndex))
{
continue;
}
if (group.signals[index].axisIndex == signal.axisIndex)
{
axisShared = true;
break;
}
}
if (axisShared)
{
// 共享轴:只裁剪采样值,时间基准保持原样(采样仍按原下标对齐,
// 因此需要把「区间内」的采样**原样**放回原位置?不——那样会让值与时间错位)。
// ⇒ 正确做法:为该信号派生一条**独立时间轴**,把它挂到新轴上。
// 这里直接返回失败,由调用方(宿主)先 `ForkAxisForSignal` 再重试。
return false;
}
{
std::vector<double> trimmedTime;
trimmedTime.swap(keptTime);