网格管理增强:新信号置底、拆分当前网格、中键自由平移、选区模式图例可用、面板悬停高亮、中心轴样式与越界三角
- 新勾选的信号所在网格固定出现在**最下方**:
原先 PER_SIGNAL 模式按 visibleRefs(文件/列序)重建网格,新勾选的信号会按其列序插到
中间,位置看起来"不确定"。现在先按「原有网格位置」排序(已分配过网格的信号保持相对次序),
再把尚未分配网格(新勾选)的信号统一追加到末尾。
- 网格右键菜单在「实验性功能」上方新增「拆分当前网格」(`PLOT_CMD_LANE_SPLIT`):
把该网格内的信号按个拆分成独立网格,新网格插入到原网格的**下方依次排列**,
原网格下方的网格顺次后移。网格编号(GX)完全由位置推导,因此左侧列表徽标、图例徽标、
面板标题、菜单标题等所有使用该编号的地方会同步更新,不会错位。
新增 `TabConfig::SplitLane()` / `PlotView::SplitLane()` / `PlotView::LaneSignalCount()`
(仅 1 条信号时菜单项置灰)。拆分出来的网格继承原网格的网格自身状态
(Y 轴自适应、边沿计数、面板位置等)。
- 鼠标**中键**按下拖拽也可自由查看区域(水平平移时间轴 + 纵向偏移当前网格幅值):
新增 `PlotView::OnMButtonDown/OnMButtonUp` 与 `Application::WM_MBUTTONDOWN/UP`,
**任何缩放模式下都可用**(不改变当前模式)。拖动期间 `WM_MOUSEMOVE` 的按键掩码
加入 `MK_MBUTTON`,光标刷新为抓握图标。
- 选区放大模式下不再阻断图例交互:把「点击图例条目 → 选中信号」的判定移到选区放大分支
**之前**,并让图例悬停(半高亮)在选区放大模式下同样生效(该模式只应改变拖拽语义)。
同时选区放大模式下也可用中键拖拽平移。
- 实验性功能浮动面板悬停时高亮其所属网格(与鼠标直接停在网格上一致):
`OnMouseMove` 中若鼠标落在面板上则把 `m_hoverLane` 置为该面板所属网格。
- 「边沿计数(中心轴)」:
* 中心平均值曲线的颜色样式改为与光标一致(`CursorColor(1)`,即「边沿计数」横向光标
与中心值胶囊的颜色),曲线与其取值标注、光标形成统一的视觉语言。
* 中心值标注形式改为「数值(中心轴)」(数值在前、尾缀在后);
双光标模式下由右侧光标额外显示增量,形如「数值(+0.0008 中心轴)」,
与常规信号的增量显示保持一致(无尖头括号)。
* 修复:光标处于当前显示区域之外(贴边显示)时,常规数据点会变成三角指示方向,
而中心轴数据点仍是圆点。现在中心轴数据点复用同一分支,越界时同样绘制三角。
验证(TestData/VERY_BIG_DATA.CSV,125%、1725x996):
- 置底:先勾选第 5 个信号(电流钳采集(原始))→ G1,再勾选第 1 个信号(编码器A相)→ G2,
新勾选的信号确实出现在最下方。
- 拆分:G1(编码器A相) / G2(飞线IO+编码器B相) / G3(电流钳(原始)) 时拆分 G2,
得到 G1 / G2(飞线IO) / G3(编码器B相) / G4(电流钳(原始)),
原本在中间的电流钳顺延为 G4,左侧徽标、图例徽标全部同步。
- 中键:常规模式与选区放大模式下中键拖拽均能把时间轴平移到 80~160(截图核对)。
- 选区放大模式:悬停图例条目显示半高亮;点击图例条目可切换选中信号(左侧高亮随之切换)。
- 面板悬停:鼠标停在 G1 的「边沿计数」面板上时 G1 区域被高亮(与悬停 G1、G2 两种情形
做区域哈希对比,确认高亮归属正确)。
- 中心轴:曲线为光标橙 `#CB4B16`;面板标题「中心轴边沿计数」、底部「平滑点数 N = 5」;
单光标显示 `4.9417` + `4.94199(中心轴)`;
双光标(标题带「(光标区间内)」,计数 341959/341957)右侧光标显示
`4.9425 (+0.000…)` + `4.94214(+0.00… 中心轴)`,左侧光标无增量;
把 N 改为 4000 后曲线与计数同步更新(2333700/2333700);
用选区放大把光标推到视野外后,中心轴数据点在与信号相同的贴边位置绘制为**橙色三角**
(像素扫描 x=479 处得到 6px 高的 `#CB4B16` 三角,而非常规圆点)。
- 回归:[1].CSV 上「边沿计数」面板正常(上升沿 2 / 下降沿 2),中键平移正常。
- make clean && make all 无警告无错误。
This commit is contained in:
1 parent
5182bd8aa3
commit
82a29362eb
6 files changed
+342
-44
No files matched your search
@@ -210,12 +210,50 @@ namespace jdi
|
||||
return fileVisible[static_cast<std::size_t>(ref.signalIndex)];
|
||||
};
|
||||
|
||||
// 某信号当前所属的网格下标(-1 表示尚未分配网格)。
|
||||
const auto laneIndexOf = [&lanes](const SignalRef& ref)
|
||||
{
|
||||
for (std::size_t index = 0; index < lanes.size(); ++index)
|
||||
{
|
||||
const std::vector<SignalRef>& refs = lanes[index].signals;
|
||||
if (std::find(refs.begin(), refs.end(), ref) != refs.end())
|
||||
{
|
||||
return static_cast<int>(index);
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
};
|
||||
|
||||
if (mode == LaneMode::PER_SIGNAL)
|
||||
{
|
||||
// 每条可见信号独占一个网格:网格数量严格跟随可见信号数量。
|
||||
lanes.clear();
|
||||
lanes.reserve(visibleRefs.size());
|
||||
// **顺序按"原有网格位置"优先**:已经分配过网格的信号保持其相对次序,
|
||||
// 新勾选(此前没有网格)的信号统一追加到最下方。
|
||||
// 若直接按 visibleRefs(文件/列序)重建,新勾选的信号会按其列序
|
||||
// 插到中间,位置看起来"不确定"。
|
||||
std::vector<SignalRef> ordered;
|
||||
ordered.reserve(visibleRefs.size());
|
||||
for (const SignalRef& ref : visibleRefs)
|
||||
{
|
||||
if (laneIndexOf(ref) >= 0)
|
||||
{
|
||||
ordered.push_back(ref);
|
||||
}
|
||||
}
|
||||
std::stable_sort(ordered.begin(), ordered.end(),
|
||||
[&laneIndexOf](const SignalRef& a, const SignalRef& b)
|
||||
{ return laneIndexOf(a) < laneIndexOf(b); });
|
||||
for (const SignalRef& ref : visibleRefs)
|
||||
{
|
||||
if (laneIndexOf(ref) < 0)
|
||||
{
|
||||
ordered.push_back(ref);
|
||||
}
|
||||
}
|
||||
|
||||
lanes.clear();
|
||||
lanes.reserve(ordered.size());
|
||||
for (const SignalRef& ref : ordered)
|
||||
{
|
||||
Lane lane;
|
||||
lane.autoRange = true;
|
||||
@@ -1169,9 +1207,64 @@ namespace jdi
|
||||
return LaneOfSignal(ref);
|
||||
}
|
||||
|
||||
bool TabConfig::IsCollapsed(int fileIndex) const
|
||||
int TabConfig::SplitLane(int laneIndex)
|
||||
{
|
||||
if (fileIndex < 0 || static_cast<std::size_t>(fileIndex) >= m_collapsed.size())
|
||||
if (laneIndex < 0 || laneIndex >= static_cast<int>(m_lanes.size()))
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
const std::vector<SignalRef> signals = m_lanes[static_cast<std::size_t>(laneIndex)].signals;
|
||||
if (signals.size() <= 1)
|
||||
{
|
||||
// 只有一条信号时无需拆分(本就独占一个网格)。
|
||||
return -1;
|
||||
}
|
||||
|
||||
// 手动排布 → 切换到自定义划分策略。
|
||||
m_laneMode = LaneMode::CUSTOM;
|
||||
|
||||
// 用原网格的“网格自身状态”(Y 轴自适应/边沿计数等)作为每一份的初始状态,
|
||||
// 这样拆分出来的网格保持原网格的显示设置。
|
||||
const Lane source = m_lanes[static_cast<std::size_t>(laneIndex)];
|
||||
|
||||
std::vector<Lane> pieces;
|
||||
pieces.reserve(signals.size());
|
||||
for (const SignalRef& ref : signals)
|
||||
{
|
||||
Lane lane;
|
||||
lane.autoRange = source.autoRange;
|
||||
lane.yMin = source.yMin;
|
||||
lane.yMax = source.yMax;
|
||||
lane.fitYToSelection = source.fitYToSelection;
|
||||
lane.edgeCount = source.edgeCount;
|
||||
lane.edgeValue = source.edgeValue;
|
||||
lane.edgeValueValid = source.edgeValueValid;
|
||||
lane.edgePanelOpen = source.edgePanelOpen;
|
||||
lane.edgePanelX = source.edgePanelX;
|
||||
lane.edgePanelY = source.edgePanelY;
|
||||
lane.edgeCenter = source.edgeCenter;
|
||||
lane.edgeCenterSmooth = source.edgeCenterSmooth;
|
||||
lane.edgeCenterPanelOpen = source.edgeCenterPanelOpen;
|
||||
lane.edgeCenterPanelX = source.edgeCenterPanelX;
|
||||
lane.edgeCenterPanelY = source.edgeCenterPanelY;
|
||||
lane.signals.push_back(ref);
|
||||
pieces.push_back(std::move(lane));
|
||||
}
|
||||
|
||||
// 用 pieces 替换原网格(插入到原位置,因此其后方的网格整体顺次后移)。
|
||||
m_lanes.erase(m_lanes.begin() + laneIndex);
|
||||
m_lanes.insert(m_lanes.begin() + laneIndex, pieces.begin(), pieces.end());
|
||||
|
||||
// 统一重命名,保证编号连续(GX 编号在各显示位置均由此位置推导)。
|
||||
for (std::size_t index = 0; index < m_lanes.size(); ++index)
|
||||
{
|
||||
m_lanes[index].name = L"网格 " + std::to_wstring(index + 1);
|
||||
}
|
||||
return laneIndex;
|
||||
}
|
||||
|
||||
bool TabConfig::IsCollapsed(int fileIndex) const
|
||||
{ if (fileIndex < 0 || static_cast<std::size_t>(fileIndex) >= m_collapsed.size())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user