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:
1 parent
62220477c0
commit
21a9865365
7 files changed
+199
-12
No files matched your search
@@ -1097,9 +1097,16 @@ namespace jdi
|
|||||||
{
|
{
|
||||||
timeShift = style->offsetTime;
|
timeShift = style->offsetTime;
|
||||||
}
|
}
|
||||||
const bool ok = m_dataSet.ClipSignalRange(groupIndex, signalIndex, low - timeShift,
|
// ⚠️⚠️ **必须先让该信号独占时间轴**再裁剪:同组信号常常共享同一条时间轴,
|
||||||
high - timeShift);
|
// 直接裁剪会把其它信号的时间基准一起改短 ⇒ 它们的采样与时间错位
|
||||||
if (!ok)
|
//(用户 2026-11-03 报告:"多个信号同时截取时截取出来的信号并不对")。
|
||||||
|
std::size_t ownAxis = 0;
|
||||||
|
if (!m_dataSet.ForkAxisForSignal(groupIndex, signalIndex, ownAxis))
|
||||||
|
{
|
||||||
|
m_plot.ShowPersistentHint(L"选区截取失败:无法为该信号创建独立时间轴。");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!m_dataSet.ClipSignalRange(groupIndex, signalIndex, low - timeShift, high - timeShift))
|
||||||
{
|
{
|
||||||
m_plot.ShowPersistentHint(L"选区截取失败:区间内的有效采样点不足两个。");
|
m_plot.ShowPersistentHint(L"选区截取失败:区间内的有效采样点不足两个。");
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -3587,6 +3587,8 @@ namespace jdi
|
|||||||
? wheelLane.edgeCountScroll
|
? wheelLane.edgeCountScroll
|
||||||
: (hoveredKind == EdgePanelKind::RESAMPLE)
|
: (hoveredKind == EdgePanelKind::RESAMPLE)
|
||||||
? wheelLane.resampleScroll
|
? wheelLane.resampleScroll
|
||||||
|
: (hoveredKind == EdgePanelKind::CLIP)
|
||||||
|
? wheelLane.clipScroll
|
||||||
: wheelLane.meanFilterScroll;
|
: wheelLane.meanFilterScroll;
|
||||||
const float next = current - static_cast<float>(notches * 40.0 * m_scale);
|
const float next = current - static_cast<float>(notches * 40.0 * m_scale);
|
||||||
const float target = std::max(0.0f, std::min(next, maxScroll));
|
const float target = std::max(0.0f, std::min(next, maxScroll));
|
||||||
|
|||||||
@@ -4209,7 +4209,9 @@ namespace jdi
|
|||||||
if (kind == EdgePanelKind::CLIP)
|
if (kind == EdgePanelKind::CLIP)
|
||||||
{
|
{
|
||||||
mix(Tab().CursorMode() == PlotCursorMode::DOUBLE ? 1ull : 0ull);
|
mix(Tab().CursorMode() == PlotCursorMode::DOUBLE ? 1ull : 0ull);
|
||||||
mix(lane.clipScroll > 0.0f ? 1ull : 0ull);
|
// ⚠️ 滚动偏移必须**按真实值**入键(不能退化成布尔):否则滚到 1px 之后
|
||||||
|
// 键不再变化 ⇒ 后续滚动不会重绘 ⇒ 表现为"滚不动 / 滚不到底"(用户 2026-11-03)。
|
||||||
|
mixFloat(lane.clipScroll);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ⚠️⚠️ **边沿统计的数字本身就是面板内容,数字变化必须能让面板重绘**:
|
// ⚠️⚠️ **边沿统计的数字本身就是面板内容,数字变化必须能让面板重绘**:
|
||||||
|
|||||||
@@ -393,12 +393,13 @@ namespace jdi
|
|||||||
// 面板底部(内容区之下)占的纵向空间:有按钮时 = pad + 按钮 + pad,无按钮时只有 pad。
|
// 面板底部(内容区之下)占的纵向空间:有按钮时 = pad + 按钮 + pad,无按钮时只有 pad。
|
||||||
// ⚠️ 「选区截取」底部是**两个**按钮行(截取为新信号 / 原始信号截取),
|
// ⚠️ 「选区截取」底部是**两个**按钮行(截取为新信号 / 原始信号截取),
|
||||||
// 因此 footer 要按两行算(用户 2026-11-03 要求)。
|
// 因此 footer 要按两行算(用户 2026-11-03 要求)。
|
||||||
|
// ⚠️ **未启用双光标**时两个按钮都不显示 ⇒ footer 归零(否则按钮位置会留一大段空白)。
|
||||||
|
const bool clipReady = clipPanel && (Tab().CursorMode() == PlotCursorMode::DOUBLE);
|
||||||
const float footerHeight =
|
const float footerHeight =
|
||||||
clipPanel ? (buttonHeight * 2.0f + 6.0f * m_scale + pad)
|
clipPanel ? (clipReady ? (buttonHeight * 2.0f + 6.0f * m_scale + pad) : 0.0f)
|
||||||
: (hasButton ? (buttonHeight + pad) : 0.0f);
|
: (hasButton ? (buttonHeight + pad) : 0.0f);
|
||||||
const float bottomPad = pad;
|
const float bottomPad = pad;
|
||||||
|
|
||||||
const float weakPeakSliderRow = kWeakPeakSliderRowHeight * m_scale;
|
|
||||||
float meanContentHeight = 0.0f;
|
float meanContentHeight = 0.0f;
|
||||||
float height = 0.0f;
|
float height = 0.0f;
|
||||||
float tableHeaderHeight = 0.0f;
|
float tableHeaderHeight = 0.0f;
|
||||||
@@ -426,8 +427,19 @@ namespace jdi
|
|||||||
// 「均值滤波」与「弱峰调整」= 两行滑块。
|
// 「均值滤波」与「弱峰调整」= 两行滑块。
|
||||||
// 内容超高时出现滚动条;底部「构建为新信号」按钮固定不滚动。
|
// 内容超高时出现滚动条;底部「构建为新信号」按钮固定不滚动。
|
||||||
const float groupRow = kEdgePanelMeanRowHeight * m_scale;
|
const float groupRow = kEdgePanelMeanRowHeight * m_scale;
|
||||||
const float sliderRow = weakPeakSliderRow;
|
const float sliderRow = kWeakPeakSliderRowHeight * m_scale;
|
||||||
|
// ⚠️ 「选区截取」在**未启用双光标**时没有任何可操作内容(只有一行提示文字),
|
||||||
|
// 因此不按信号数累加行高、也不套用「至少 3 行」的下限
|
||||||
|
// (用户 2026-11-03 报告:未启用双光标时仍显示信号条目、且能触发滚动)。
|
||||||
|
const bool clipReady = clipPanel && (Tab().CursorMode() == PlotCursorMode::DOUBLE);
|
||||||
float content = 0.0f;
|
float content = 0.0f;
|
||||||
|
if (clipPanel && !clipReady)
|
||||||
|
{
|
||||||
|
// 提示行固定占一行高度(绘制时在内容区居中显示)。
|
||||||
|
content = kEdgePanelMeanRowHeight * m_scale;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
for (const SignalRef& ref : lane.signals)
|
for (const SignalRef& ref : lane.signals)
|
||||||
{
|
{
|
||||||
const ResolvedSignal resolved = ResolveSignal(ref);
|
const ResolvedSignal resolved = ResolveSignal(ref);
|
||||||
@@ -478,12 +490,15 @@ namespace jdi
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
meanContentHeight = content;
|
meanContentHeight = content;
|
||||||
const float budget =
|
const float budget =
|
||||||
m_plotRect.Height - (titleHeight + pad + bottomPad + footerHeight);
|
m_plotRect.Height - (titleHeight + pad + bottomPad + footerHeight);
|
||||||
|
// ⚠️ 高度必须**精确等于内容高度**(不超过可用预算),不要再套「至少 3 行」的下限:
|
||||||
|
// 那个下限会让只有 1 个信号的面板留出 2 行空白(用户 2026-11-03 报告
|
||||||
|
// “信号条目的滚动区域与下方按钮之间有一大段空白”)。
|
||||||
const float maxContent =
|
const float maxContent =
|
||||||
std::max(3.0f * groupRow,
|
std::min(kFreeMarkerContentMaxHeight * m_scale, std::max(groupRow, budget));
|
||||||
std::min(kFreeMarkerContentMaxHeight * m_scale, std::max(3.0f * groupRow, budget)));
|
|
||||||
const float view = std::min(content, maxContent);
|
const float view = std::min(content, maxContent);
|
||||||
height = titleHeight + pad + view + bottomPad + footerHeight;
|
height = titleHeight + pad + view + bottomPad + footerHeight;
|
||||||
}
|
}
|
||||||
@@ -811,11 +826,15 @@ namespace jdi
|
|||||||
const float groupRow = kEdgePanelMeanRowHeight * m_scale;
|
const float groupRow = kEdgePanelMeanRowHeight * m_scale;
|
||||||
const float sliderRow = kWeakPeakSliderRowHeight * m_scale;
|
const float sliderRow = kWeakPeakSliderRowHeight * m_scale;
|
||||||
const float scrollW = kFreeMarkerScrollWidth * m_scale;
|
const float scrollW = kFreeMarkerScrollWidth * m_scale;
|
||||||
|
const bool clipReadyHere =
|
||||||
|
clipPanel && (Tab().CursorMode() == PlotCursorMode::DOUBLE);
|
||||||
const float footerHeight =
|
const float footerHeight =
|
||||||
clipPanel ? (buttonRowHeight * 2.0f + 6.0f * m_scale + pad)
|
clipPanel ? (clipReadyHere ? (buttonRowHeight * 2.0f + 6.0f * m_scale + pad) : 0.0f)
|
||||||
: (hasButton ? (buttonRowHeight + pad) : 0.0f);
|
: (hasButton ? (buttonRowHeight + pad) : 0.0f);
|
||||||
const float viewHeight =
|
// ⚠️ 内容区高度必须与高度算式(`height = title + pad + view + bottomPad + footer`)
|
||||||
std::max(0.0f, height - titleHeight - pad - pad - footerHeight);
|
// 严格同源:只减 **一个** pad(标题下方那次),不要再多减一个,否则内容区偏矮、
|
||||||
|
// 滚动条提前出现且底部留出空白(用户 2026-11-03 报告)。
|
||||||
|
const float viewHeight = std::max(0.0f, height - titleHeight - pad - footerHeight);
|
||||||
const bool scrollable = meanContentHeight > viewHeight + 0.5f;
|
const bool scrollable = meanContentHeight > viewHeight + 0.5f;
|
||||||
const float maxScroll = std::max(0.0f, meanContentHeight - viewHeight);
|
const float maxScroll = std::max(0.0f, meanContentHeight - viewHeight);
|
||||||
const float storedScroll = meanFilter ? lane.meanFilterScroll
|
const float storedScroll = meanFilter ? lane.meanFilterScroll
|
||||||
@@ -871,8 +890,15 @@ namespace jdi
|
|||||||
(resetLeft - resetGap - valueColumn - 6.0f * m_scale) - trackLeft);
|
(resetLeft - resetGap - valueColumn - 6.0f * m_scale) - trackLeft);
|
||||||
|
|
||||||
float rowY = layout.content.Y - scroll;
|
float rowY = layout.content.Y - scroll;
|
||||||
|
// ⚠️ 「选区截取」在**未启用双光标**时不生成任何信号行:面板只显示一句居中提示
|
||||||
|
// (用户 2026-11-03 明确要求:不应显示信号名条目、更不应能滚动)。
|
||||||
|
const bool clipRowsVisible = !clipPanel || clipReadyHere;
|
||||||
for (const SignalRef& ref : lane.signals)
|
for (const SignalRef& ref : lane.signals)
|
||||||
{
|
{
|
||||||
|
if (!clipRowsVisible)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
const ResolvedSignal resolved = ResolveSignal(ref);
|
const ResolvedSignal resolved = ResolveSignal(ref);
|
||||||
if (resolved.data == nullptr || !resolved.visible)
|
if (resolved.data == nullptr || !resolved.visible)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -9952,3 +9952,66 @@ MEAN_SCROLL / 滚轮 / 悬停 / `SetLanePanelCollapsed` / `LanePanelCollapsed` /
|
|||||||
本轮因此连续 3 次编译失败。**改这类表达式时先用 Python 数列数括号**,或改成查表。
|
本轮因此连续 3 次编译失败。**改这类表达式时先用 Python 数列数括号**,或改成查表。
|
||||||
* `make clean` 在用户正开着 `Target.exe` 时会失败(文件被占用),
|
* `make clean` 在用户正开着 `Target.exe` 时会失败(文件被占用),
|
||||||
改用"删 `Build/Release/*.o` + `make`"做全量重编译验证即可。
|
改用"删 `Build/Release/*.o` + `make`"做全量重编译验证即可。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2026-11-03 第二十八轮(本机 dev):选区截取面板五处缺陷修复
|
||||||
|
|
||||||
|
> 用户实测报告的五个问题:①未启用双光标时仍显示信号条目、过多信号还能滚动;
|
||||||
|
> ②条目滚动区与底部按钮之间有一大段空白(未启用双光标时也有);
|
||||||
|
> ③多个信号同时「原始信号截取」结果不对;④有滚动条时滚轮滚不到底。
|
||||||
|
|
||||||
|
### 1. 未启用双光标时不应显示信号条目
|
||||||
|
|
||||||
|
**根因**:`EdgePanelLayoutFor` 与绘制侧都只判断 `clipPanel`,没有判断「双光标是否启用」,
|
||||||
|
于是照常按信号数累加内容高度、生成行矩形、绘制条目。
|
||||||
|
|
||||||
|
**修法**(`PlotViewPanels.cpp`):
|
||||||
|
* 高度算式新增 `clipReady = clipPanel && CursorMode()==DOUBLE`:
|
||||||
|
未就绪时 `content = 一行高度`(只放提示),不遍历信号。
|
||||||
|
* 行几何循环加 `clipRowsVisible = !clipPanel || clipReadyHere`,未就绪时**不生成任何行矩形**
|
||||||
|
⇒ 绘制侧(依赖 `layout.rowRefs`)自然什么都不画。
|
||||||
|
* footer 也改为「未就绪 = 0」:`clipPanel ? (clipReady ? 两行按钮 : 0) : ...`。
|
||||||
|
|
||||||
|
### 2. ⭐ 条目区与按钮之间的大段空白
|
||||||
|
|
||||||
|
**两个叠加根因**:
|
||||||
|
1. 高度算式里 `maxContent = max(3*groupRow, min(上限, max(3*groupRow, budget)))`
|
||||||
|
—— 那个「至少 3 行」的下限让只有 1 个信号的面板硬撑到 3 行高。
|
||||||
|
⇒ 改为 `maxContent = min(上限, max(groupRow, budget))`(只保留 1 行下限)。
|
||||||
|
2. 几何侧 `viewHeight = height - titleHeight - pad - pad - footerHeight` **多减了一个 pad**,
|
||||||
|
而高度算式是 `height = title + pad + view + bottomPad + footer`(`bottomPad = pad`)。
|
||||||
|
⇒ 统一为 `viewHeight = height - titleHeight - pad - footerHeight`。
|
||||||
|
⚠️ **高度算式与内容区几何必须严格同源**,多减一个 pad 就会在底部留出一条空白。
|
||||||
|
|
||||||
|
### 3. ⭐⭐ 多个信号「原始信号截取」结果不对
|
||||||
|
|
||||||
|
**根因**:同组的信号**共享同一条时间轴**(合并网格/同文件多通道)。
|
||||||
|
`DataSet::ClipSignalRange` 每次截取都会裁剪 `axes[signal.axisIndex].time`
|
||||||
|
⇒ 截完第一条,后面信号的时间基准已被改短 ⇒ 采样与时间错位("截取出来的信号不对")。
|
||||||
|
|
||||||
|
**修法**:
|
||||||
|
* 新增 `DataSet::ForkAxisForSignal(group, signal, &axisIndex)`:
|
||||||
|
若该信号的时间轴**不是独占**,就复制一份新轴并把信号指向它(已独占则直接返回)。
|
||||||
|
* `ClipSignalRange` 内再兜一层:检测到轴仍被其它信号共享时**直接返回 false**(不改任何数据),
|
||||||
|
避免任何调用方绕过 fork 造成错位。
|
||||||
|
* `Application::ClipSignalInPlace`:**先** `ForkAxisForSignal` 再 `ClipSignalRange`。
|
||||||
|
* 降序处理多个信号时,每条信号各自 fork 出自己的轴,互不影响。
|
||||||
|
|
||||||
|
### 4. ⭐ 有滚动条时滚轮滚不到底
|
||||||
|
|
||||||
|
**根因**(典型的三元式漏分支):
|
||||||
|
`OnMouseWheel` 的 `current` 取值链只列了 CENTER/PEAK/COUNT/RESAMPLE,**漏了 CLIP**
|
||||||
|
⇒ 读到的是 `meanFilterScroll`(恒 0),而写入的是 `clipScroll`
|
||||||
|
⇒ 每次滚轮都从 0 开始算,永远只能滚一小步、滚不到底部。
|
||||||
|
⇒ 补上 `(hoveredKind == CLIP) ? wheelLane.clipScroll : ...`。
|
||||||
|
|
||||||
|
**并发现第二个隐患**:`PanelSlotKey` 里 CLIP 的滚动量被写成
|
||||||
|
`mix(lane.clipScroll > 0.0f ? 1 : 0)`(布尔)⇒ 滚到 1px 之后键不再变化、面板不重绘。
|
||||||
|
⇒ 改为 `mixFloat(lane.clipScroll)`(真实值)。
|
||||||
|
|
||||||
|
### 5. 验证与提交
|
||||||
|
|
||||||
|
* 三处 `switch/tag` 均已逐个 grep 核对(`EdgePanelKind::RESAMPLE` 反查法)。
|
||||||
|
* 删 `.o` 全量重编译:**零 error、零 warning**,产物 861,184 B(< 1MB)。
|
||||||
|
* 用户要求不占用鼠标键盘屏幕 ⇒ 本轮**未跑任何 GUI 探针**,仅做编译验证与源码级核对。
|
||||||
@@ -689,6 +689,23 @@ namespace jdi
|
|||||||
*/
|
*/
|
||||||
bool ClipSignalRange(int groupIndex, int signalIndex, double low, double high);
|
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 移除指定下标的分组,并立即释放其占用的全部采样内存。
|
* @brief 移除指定下标的分组,并立即释放其占用的全部采样内存。
|
||||||
* @param [IN] groupIndex 分组下标。
|
* @param [IN] groupIndex 分组下标。
|
||||||
|
|||||||
@@ -499,6 +499,50 @@ namespace jdi
|
|||||||
return true;
|
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)
|
bool DataSet::ClipSignalRange(int groupIndex, int signalIndex, double low, double high)
|
||||||
{
|
{
|
||||||
if (groupIndex < 0 || groupIndex >= static_cast<int>(m_groups.size()))
|
if (groupIndex < 0 || groupIndex >= static_cast<int>(m_groups.size()))
|
||||||
@@ -545,6 +589,32 @@ namespace jdi
|
|||||||
signal.values.swap(trimmed);
|
signal.values.swap(trimmed);
|
||||||
signal.values.shrink_to_fit();
|
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;
|
std::vector<double> trimmedTime;
|
||||||
trimmedTime.swap(keptTime);
|
trimmedTime.swap(keptTime);
|
||||||
|
|||||||
Reference in new issue
Block a user