主题自适应配色 + 预览曲线连续配色 + 样式区复位按钮 + 均值滤波面板重做 + 面板缓存环带残留 + 调试层序 + 自由标记选取与性能
① 主题自适应(`Theme.cpp`):新增 `SrgbLuminance` / `ContrastRatio` / `EnsureContrast` /
`MixToLuminance` / `ThemeLaneBackground`;四套主题的结构色(gridWeak 1.30 / gridStrong 1.65 /
axisLine 2.10 / borderStrong 1.70)按底色自动校正,Solarised Light 逐位不变;网格叠底改用
`ThemeLaneBackground()`(4 处),深色主题下网格层与绘图区底色可辨。
② 派生预览曲线(`HighContrastDerived`)重写为**连续**方案:方向沿信号所在侧远离底色,
目标亮度 = max(与底色对比度 3.2, 信号亮度 ± 0.18),再按亮度平滑混色;调用点传入真实网格底色
(旧实现默认写死 Solarised Light 底色 ⇒ 深色主题永远选到更暗的那一侧、几乎看不见)。
③ 信号样式区「线宽 / 透明度」两行右侧新增**复位按钮**(`UiPaintResetIcon` 成为唯一实现,
菜单/弱峰面板/样式区共用同一套笔画);默认线宽统一到 `DefaultSignalLineWidth(finiteCount)`
(单点信号取 10.0)。按钮右边界与内容列对齐,内容列过窄时整列不显示。
④ 「均值滤波」面板重做:信号色叠底标题栏 + 复选框 + 展开三角 + 「均值点数 / 迭代次数」两行滑块
(点击数值可改滑块上限、右侧复位)+ 内容区滚动;新增 `MeanFilterItem::{maxPoints,maxIterations,
collapsed}` 与 `Lane::meanFilterScroll`,缓存键同步补全。
⑤ 浮动面板命中像素缓存时只贴**面板圆角形状之内**的像素(`CreateRoundRectRgn` 裁剪)并按本帧重画投影
⇒ 中键移视界后面板周围不再出现一圈旧波形。
⑥ 显示布局边界:每个浮动面板**独占一个绘制层**(按 `m_panelOrder` 递增),绘制侧
`UiSetTextLayer` 在同一循环内同步推进 ⇒ 下层面板的边框/文字不再画到上层面板上。
⑦ 自由标记:采样候选距离改为**二维欧氏距离**(单点信号不再在整幅宽度内吸住光标),
纵向光标画在鼠标位置、圆点落在真实采样点(新增 `FreeMarkerProbe::pointX`);
`MinorCellSparse` 只对胜出信号算一次;折线缓存容量改为 `max(64, 信号数×2)` 且查找改为
单键哈希 ⇒ MF4 的 285 信号合并网格中 `ProbeFreeMarker` 平均 0.11ms。
验证:`.preflight` 探针重建(`panelcheck` 71/71、`markercheck` 13/13、`ringcheck` 6/6,合计 90 条,
LTO 目标文件上复跑同样全绿);`make release` / `make debug` 零警告;产物 815,104 B。
文档:`agent_mem.md` 第十六轮(含补丁脚本与编码/行尾踩坑)。
This commit is contained in:
1 parent
71195c4c48
commit
bdd703b88b
24 files changed
+1815
-256
No files matched your search
@@ -200,36 +200,8 @@ namespace jdi
|
||||
inline void PaintPanelResetIcon(gd::Graphics& g, const gd::RectF& rect, std::uint32_t color,
|
||||
float scale)
|
||||
{
|
||||
const float inset = 2.5f * scale;
|
||||
gd::RectF box(rect.X + inset, rect.Y + inset, rect.Width - inset * 2.0f,
|
||||
rect.Height - inset * 2.0f);
|
||||
if (box.Width <= 1.0f || box.Height <= 1.0f)
|
||||
{
|
||||
return;
|
||||
}
|
||||
gd::Pen pen(gd::Color((color >> 24) & 0xFFu, (color >> 16) & 0xFFu, (color >> 8) & 0xFFu,
|
||||
color & 0xFFu),
|
||||
std::max(1.0f, 1.5f * scale));
|
||||
pen.SetStartCap(gd::LineCapRound);
|
||||
pen.SetEndCap(gd::LineCapRound);
|
||||
// 缺口留在右上角(从 300° 到 20° 处断开),与常见的"重置/刷新"图标一致。
|
||||
g.DrawArc(&pen, box.X, box.Y, box.Width, box.Height, 300.0f, 300.0f);
|
||||
|
||||
// 箭头:位于缺口末端(约 300° 方向),指向前进方向。
|
||||
const float cx = box.X + box.Width * 0.5f;
|
||||
const float cy = box.Y + box.Height * 0.5f;
|
||||
const float radius = box.Width * 0.5f;
|
||||
const float angle = 300.0f * 3.14159265f / 180.0f;
|
||||
const float tipX = cx + std::cos(angle) * radius;
|
||||
const float tipY = cy + std::sin(angle) * radius;
|
||||
const float size = std::max(2.5f, 3.2f * scale);
|
||||
gd::PointF arrow[3];
|
||||
arrow[0] = gd::PointF(tipX, tipY);
|
||||
arrow[1] = gd::PointF(tipX - size, tipY - size * 0.25f);
|
||||
arrow[2] = gd::PointF(tipX - size * 0.25f, tipY - size);
|
||||
gd::SolidBrush brush(gd::Color((color >> 24) & 0xFFu, (color >> 16) & 0xFFu,
|
||||
(color >> 8) & 0xFFu, color & 0xFFu));
|
||||
g.FillPolygon(&brush, arrow, 3);
|
||||
// 唯一实现见 `PaintUtil.hpp`(弹出菜单的还原图标、信号样式区的复位按钮共用同一套笔画)。
|
||||
UiPaintResetIcon(g, rect, color, scale);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -579,6 +551,28 @@ namespace jdi
|
||||
inline const wchar_t* const kWeakPeakIterationsSliderLabel = L"迭代次数";
|
||||
inline const wchar_t* const kWeakPeakTitle = L"弱峰调整"; /**< 弱峰调整面板标题。 */
|
||||
inline const wchar_t* const kBuildSignalLabel = L"构建为新信号"; /**< 逐条目面板底部按钮文案(均值滤波 / 弱峰调整共用)。 */
|
||||
|
||||
// ---- 「均值滤波」面板的两行参数滑块(用户 2026-10-30 要求)----
|
||||
/**
|
||||
* 标签一律**不带 `X` / `N`**:滑块的量程本身已经说明是数值,符号只会让标签变长。
|
||||
* (信号样式区与菜单里仍保留 `均值点数 X` / `迭代次数 N` 的写法,那是输入框语义。)
|
||||
*/
|
||||
inline const wchar_t* const kMeanFilterPointsSliderLabel = L"均值点数";
|
||||
inline const wchar_t* const kMeanFilterIterationsSliderLabel = L"迭代次数";
|
||||
/** 「均值点数」下限(窗口 = 2X+1 ⇒ X=0 就是原信号)。 */
|
||||
inline const int kMeanFilterPointsMin = 0;
|
||||
/** 「均值点数」滑块的默认上限(用户可点击数值改成其它正整数)。 */
|
||||
inline const int kMeanFilterPointsDefaultMax = 50;
|
||||
/** 「均值点数」上限允许的最大值(防止输入一个天文数字把滤波窗口撑爆)。 */
|
||||
inline const int kMeanFilterPointsMaxLimit = 100000;
|
||||
/** 新建条目时的默认均值点数(与 `Lane::MeanFilterItem::points` 初值一致)。 */
|
||||
inline const int kMeanFilterPointsDefault = 5;
|
||||
/** 「迭代次数」下限。 */
|
||||
inline const int kMeanFilterIterationsMin = 1;
|
||||
/** 「迭代次数」滑块的默认上限。 */
|
||||
inline const int kMeanFilterIterationsDefaultMax = 10;
|
||||
/** 「迭代次数」上限允许的最大值。 */
|
||||
inline const int kMeanFilterIterationsMaxLimit = 1000;
|
||||
/** 「自由标记」面板底部「自由标记点选取」切换按钮文案(激活态 = 光标模式开启)。 */
|
||||
inline const wchar_t* const kFreeMarkerCursorModeLabel = L"自由标记点选取";
|
||||
/** 「自由标记」面板底部「显示增量」切换按钮文案(只影响已固定点的显示)。 */
|
||||
@@ -1235,39 +1229,77 @@ namespace jdi
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 由信号色派生一个「高对比度」颜色:保持色相、大幅拉开明度。
|
||||
* @brief 派生预览曲线相对底色的**最低对比度**(WCAG 比值)。
|
||||
*
|
||||
* 用途:「边沿计数(中心轴)」的平均值曲线叠加在原始波形之上,
|
||||
* 若直接用信号色会被波形淹没。这里把信号色向黑或白方向大幅偏移,
|
||||
* 并**同时考虑与原始波形色以及与绘图区底色的距离**,取对比更强的一侧,
|
||||
* 保证曲线既能与波形区分、又能压在底图上看得清。
|
||||
*
|
||||
* 结果是从信号色派生的,因此原信号改颜色时曲线颜色会自动跟着变。
|
||||
*
|
||||
* @param [IN] color 原始信号色(0xAARRGGBB)。
|
||||
* @param [IN] background 绘图区底色,用于评估可见度。
|
||||
* @return 派生出的高对比度颜色(alpha 与输入一致,由调用方再覆盖)。
|
||||
* 用比值而不是"亮度差":同样的亮度差在很亮的底色上(白)几乎看不出来,
|
||||
* 在深色底上却很显眼 —— 比值才是一致可见性的正确度量。
|
||||
*/
|
||||
inline 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;
|
||||
};
|
||||
inline const double kDerivedPreviewContrast = 3.2;
|
||||
|
||||
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;
|
||||
/**
|
||||
* @brief 派生预览曲线与**源信号色**之间还要拉开的亮度差。
|
||||
*
|
||||
* 源信号本身已经与底色有足够对比时,预览曲线再往外走这么多,避免"两条线同色叠在一起"。
|
||||
*/
|
||||
inline const double kDerivedPreviewGap = 0.18;
|
||||
|
||||
/**
|
||||
* @brief 由信号色派生「派生预览曲线」的颜色(与底色、与源信号同时保持足够对比)。
|
||||
*
|
||||
* 规则(**连续**,不含任何"临界点切换"):
|
||||
* 1. 方向:沿"信号色相对底色的那一侧"继续远离底色
|
||||
*(深底上的信号往更亮走、浅底上的信号往更暗走);
|
||||
* 2. 先求"与底色对比度达到 `kDerivedPreviewContrast` 的亮度"(`EnsureContrast`),
|
||||
* 再与"信号亮度 + `kDerivedPreviewGap`"取**更远离**底色的那个;
|
||||
* 3. 用 `MixToLuminance` 把信号色沿亮度轴平滑推到该目标(保持色相)。
|
||||
*
|
||||
* 步 2 的两个候选都是输入的**连续函数**(取 max/min),因此拖拾色器 / 换预设色块 /
|
||||
* 切主题时派生色**连续变化**,不会像旧实现那样"混 72% 黑还是白"按评分在阈值处跳变。
|
||||
*
|
||||
* @param [IN] color 源信号色(0xAARRGGBB,alpha 被忽略)。
|
||||
* @param [IN] background 网格**底色**(填充 `ThemeLaneBackground`)。
|
||||
* @return 派生颜色(不透明;透明度由调用方按信号不透明度重新叠加)。
|
||||
*/
|
||||
inline std::uint32_t HighContrastDerived(std::uint32_t color, std::uint32_t background)
|
||||
{
|
||||
const std::uint32_t base = color | 0xFF000000u;
|
||||
const std::uint32_t back = background | 0xFF000000u;
|
||||
const double backgroundLuminance = SrgbLuminance(back);
|
||||
const double signalLuminance = SrgbLuminance(base);
|
||||
|
||||
// 方向:沿信号所在的"那一侧"继续远离底色。信号与底色几乎同亮度时
|
||||
// 按底色的明暗选一侧(此时源曲线本身几乎看不见,方向没有更好选择)。
|
||||
double direction = 0.0;
|
||||
if (signalLuminance > backgroundLuminance + 1.0e-4)
|
||||
{
|
||||
direction = 1.0;
|
||||
}
|
||||
else if (signalLuminance < backgroundLuminance - 1.0e-4)
|
||||
{
|
||||
direction = -1.0;
|
||||
}
|
||||
else
|
||||
{
|
||||
direction = (backgroundLuminance < 0.5) ? 1.0 : -1.0;
|
||||
}
|
||||
|
||||
// 候选一:与底色对比度达到阈值的那个颜色(已达标时就是原色)。
|
||||
const std::uint32_t required = EnsureContrast(back, base, kDerivedPreviewContrast);
|
||||
const double requiredLuminance = SrgbLuminance(required);
|
||||
// 候选二:在信号亮度的基础上再拉开一段。
|
||||
const double gapLuminance = signalLuminance + direction * kDerivedPreviewGap;
|
||||
|
||||
double target = (direction > 0.0) ? std::max(requiredLuminance, gapLuminance)
|
||||
: std::min(requiredLuminance, gapLuminance);
|
||||
if (target > 1.0)
|
||||
{
|
||||
target = 1.0;
|
||||
}
|
||||
if (target < 0.0)
|
||||
{
|
||||
target = 0.0;
|
||||
}
|
||||
return MixToLuminance(base, target);
|
||||
}
|
||||
/** @brief FNV-1a 混合(面板像素缓存键共用)。 */
|
||||
inline void MixPanelKey(std::uint64_t& hash, std::uint64_t value)
|
||||
|
||||
Reference in new issue
Block a user