中心轴曲线配色 + 筛选框交互完善 + 双光标拖拽性能修复

- 中心轴边沿计数的平均值曲线改为「由信号色派生的高对比度颜色」:保持色相、
  大幅拉开明度(与原始波形高对比),透明度与原信号一致;原信号改色时曲线
  颜色随之变化,而光标显示该曲线数值时仍沿用原始信号的配色样式。
- 信号筛选框:内容区左右内缩改为与上下一致(不再「左右空隙明显更大」);
  有输入时右侧显示清空 ✕(悬停高亮 + 手型光标,点击清空且保持编辑态);
  筛选无结果时在列表区提示「无匹配的筛选信号」。
- 光标数值超出上下视界时,三角箭头标识下移 kCursorHandleReserve,
  不再遮挡光标自身的三角把手(含双光标模式)。
- 性能:新增「装饰区域脏标记 + 绘图区局部回送」快路径——绘图区内的悬停/拖拽
  期间跳过标签栏/工具栏/面板/分隔条的重绘,且只 BitBlt 绘图区矩形;
  WM_PAINT 依据更新区域是否落在绘图区内决定快慢路径,窗口缩放/遮挡露出/
  菜单与拾色器弹出等场景仍整帧重绘。
  以 VERY_BIG_DATA.CSV 全信号双光标拖拽实测:单帧耗时由约 28ms 降至约 12ms。
  同时修复拖拽期间残留的信号悬停提示气泡。
This commit is contained in:
DeepSeek V4.1 Flash committed 2026-10-09 19:10:43 +08:00
1 parent fe72afc83c
commit 8ae8d7af4f
6 files changed
+333 -45

No files matched your search

+58 -11
View File
@@ -28,6 +28,7 @@ namespace jdi
const size_t kPolylinePointsPerPixel = 2; /**< 每个像素列允许的折线顶点数(上/下包络两顶点)。 */
const size_t kMinPolylinePoints = 512; /**< 折线最少顶点数(窗口很窄时仍保留细节)。 */
const float kEnvelopeMinPixelSpan = 0.5f; /**< 包络纵向跨度小于该像素值时合并为单个顶点。 */
const float kCursorHandleReserve = 9.0f; /**< 光标顶部三角把手占用的高度(数据点贴边时需避让)。 */
const size_t kMaxScatterPoints = 2500; /**< 单条散点最大绘制点数。 */
const size_t kPolylineCacheLimit = 64; /**< 折线顶点缓存条目上限。 */
const float kMinScatterRadius = 1.6f; /**< 散点最小半径(逻辑像素)。 */
@@ -294,6 +295,42 @@ namespace jdi
}
return WithAlpha(style.color, static_cast<unsigned int>(opacity * 255.0 + 0.5));
}
/**
* @brief 由信号色派生一个「高对比度」颜色:保持色相、大幅拉开明度。
*
* 用途:「边沿计数(中心轴)」的平均值曲线叠加在原始波形之上,
* 若直接用信号色会被波形淹没。这里把信号色向黑或白方向大幅偏移,
* 并**同时考虑与原始波形色以及与绘图区底色的距离**,取对比更强的一侧,
* 保证曲线既能与波形区分、又能压在底图上看得清。
*
* 结果是从信号色派生的,因此原信号改颜色时曲线颜色会自动跟着变。
*
* @param [IN] color 原始信号色(0xAARRGGBB)。
* @param [IN] background 绘图区底色,用于评估可见度。
* @return 派生出的高对比度颜色(alpha 与输入一致,由调用方再覆盖)。
*/
std::uint32_t HighContrastDerived(std::uint32_t color, std::uint32_t background = 0xFFFDF6E3u)
{
const auto squaredDistance = [](std::uint32_t a, std::uint32_t b)
{
const double dr =
static_cast<double>((a >> 16) & 0xFFu) - static_cast<double>((b >> 16) & 0xFFu);
const double dg =
static_cast<double>((a >> 8) & 0xFFu) - static_cast<double>((b >> 8) & 0xFFu);
const double db = static_cast<double>(a & 0xFFu) - static_cast<double>(b & 0xFFu);
return dr * dr + dg * dg + db * db;
};
const std::uint32_t darker = MixColor(color, 0xFF000000u, 0.72);
const std::uint32_t lighter = MixColor(color, 0xFFFFFFFFu, 0.72);
// 评分:与原始波形色的区分度为主(权重 1.0),与底色的可见度为辅(权重 0.6)。
const double scoreDarker =
squaredDistance(darker, color) + 0.6 * squaredDistance(darker, background);
const double scoreLighter =
squaredDistance(lighter, color) + 0.6 * squaredDistance(lighter, background);
return (scoreDarker >= scoreLighter) ? darker : lighter;
}
}
PlotView::PlotView() = default;
@@ -5218,7 +5255,9 @@ namespace jdi
const float rawY = ValueToY(value, lane);
// 上下越界的数据点不再被丢弃:与左右越界一致,贴边绘制指示方向的三角。
const DataMarker marker = MakeDataMarker(rawX, rawY, pinX, clipTop, clipBottom);
// 上边界额外预留光标顶部把手的高度,避免数据点三角压住光标把手。
const DataMarker marker =
MakeDataMarker(rawX, rawY, pinX, clipTop, clipBottom, kCursorHandleReserve * m_scale);
const std::uint32_t signalColor = StyleColor(*resolved.style);
@@ -5276,8 +5315,8 @@ namespace jdi
if (CenterValueAt(*resolved.file, *resolved.data, smooth, time, centerValue))
{
const float rawCenterY = ValueToY(centerValue, lane);
const DataMarker centerMarker =
MakeDataMarker(rawX, rawCenterY, pinX, clipTop, clipBottom);
const DataMarker centerMarker = MakeDataMarker(
rawX, rawCenterY, pinX, clipTop, clipBottom, kCursorHandleReserve * m_scale);
{
// 中心轴数据点的配色与原始信号一致(便于一一对应);
// 但**始终按线段模式**绘制(不跟随原信号的散点模式)。
@@ -5514,13 +5553,14 @@ namespace jdi
}
PlotView::DataMarker PlotView::MakeDataMarker(float rawX, float rawY, float pinX, float clipTop,
float clipBottom) const
float clipBottom, float topReserve) const
{
DataMarker marker;
// 上下越界时同样"钉"在网格可见边缘(留出三角形的高度,保证整体可见)。
// 上下越界时"钉"在网格可见边缘(留出三角形的高度,保证整体可见);
// 另外预留 topReserve(时间光标的顶部三角把手),避免数据点三角压住把手。
const float margin = 7.0f * m_scale;
marker.x = pinX;
marker.y = std::max(clipTop + margin, std::min(rawY, clipBottom - margin));
marker.y = std::max(clipTop + margin + topReserve, std::min(rawY, clipBottom - margin));
marker.offLeft = (rawX < marker.x - 0.5f);
marker.offRight = (rawX > marker.x + 0.5f);
marker.offUp = (rawY < marker.y - 0.5f);
@@ -5722,13 +5762,13 @@ namespace jdi
return;
}
// 中心平均值曲线:与原始信号**同色**,只是更透明一些;线宽等其它样式同样跟随原信号,
// 因此原信号改颜色/线宽/透明度时曲线会自动同步。
// 中心平均值曲线:与原信号**同色相**但大幅拉开明度,使其与原始波形形成高对比
// (原信号改颜色时曲线随之改变,因为它是从信号色派生出来的);
// 透明度与原信号保持一致。
const unsigned int baseAlpha = static_cast<unsigned int>(
std::max(0.0, std::min(1.0, static_cast<double>(resolved.style->opacity))) * 255.0 + 0.5);
const std::uint32_t derived =
WithAlpha(StyleColor(*resolved.style),
static_cast<unsigned int>(static_cast<double>(baseAlpha) * 0.55 + 0.5));
const std::uint32_t derived = WithAlpha(HighContrastDerived(StyleColor(*resolved.style)),
baseAlpha);
gd::Pen pen(ToGdiColor(derived), std::max(1.0f, resolved.style->lineWidth) * m_scale);
pen.SetLineJoin(gd::LineJoinRound);
pen.SetStartCap(gd::LineCapRound);
@@ -6137,6 +6177,13 @@ namespace jdi
return;
}
// 其它拖拽(光标/平移/中键/横向光标/面板)期间不显示信号悬停提示:
// 这些操作不会更新 m_hoverSignal,按下前的值会残留,表现为提示气泡"挂在原地"。
if (m_dragMode != PlotDragMode::NONE)
{
return;
}
// 悬停提示:光标贴近曲线时给出可拖拽说明(与时间光标的提示方式一致)。
if (!m_hoverSignal.IsValid())
{