边沿计数改用「绘制值」+ 派生信号烘入时间偏移 + 图例托底统一为 GX 徽标款 + 调整二级面板向上展开
一、两个「边沿计数」按「画出来的曲线」判定(原先按原始采样值 → 应用过「调整」的信号恒为 0)
- 用户复述的权威定义:设定一个横向(Y 轴)光标,信号绘制出来的曲线从下往上穿过该幅值 = 上升沿,
从上往下穿过 = 下降沿。
- 症状:VERY_BIG_DATA 的「编码器B相」设系数 50 + 时间轴偏移 -20s,把光标拖到画面中间,
很规整的方波却统计出 0/0。
- 根因:EdgeCountsFor / EdgeCenterCountsFor 只比较 signal.values 的原始采样值,而横向光标读的是
屏幕上的值(已乘系数、已加偏移)→ 原始值域与阈值完全不相交。
- 修法:判定值一律取 ApplyDrawTransform(style, values[SourceIndexFor(style, count, index)]);
* 中心轴两侧必须套用同一套变换(drawn - drawnCenter);
* 水平镜像时曲线数组按源下标存储、而统计区间是显示下标 → 请求曲线窗口前必须把
[needFirst, needEnd) 映射为 [count-needEnd, count-needFirst);
* 缓存必须带样式指纹:EdgeCountEntry / EdgeCenterCountEntry 新增 styleKey
(EdgeStyleKey():gain/offsetY 按 1e6 定点 + absolute/mirrorVertical/mirrorHorizontal 位),
否则改完「调整」仍会命中变换前的旧计数。
二、时间区间之外的采样不得计入(区间边界点)
- 症状:A 在阈值以下、B 超过阈值;双光标下一个光标正好在 B 点,旧实现把 B 算成一个上升沿。
- 根因:EdgeIndexRange 在左端多取了一个采样点(if (begin > 0) --begin;)。
- 修法:删掉该外扩,收紧为「只有两个端点都落在 [begin, end) 内的采样对才计入」
(begin = lower_bound(rangeMin)、end = upper_bound(rangeMax),调用方从 begin+1 枚举到 end-1)。
- 结果:把区间起点从 times[crossing-1] 移到 times[crossing] 恰好少一个上升沿。
三、派生信号把 offsetTime 也烘进采样值(推翻 §9.19 的「唯一例外」)
- 用户要求:新信号默认不启用任何「调整」时就必须与网格里的预览逐点重合。
- 修法(Application::BuildDerivedSignal,在「值变换 + 水平镜像反序」之后追加一步):
新信号第 i 个采样 = 已变换的曲线在 time[i] - offsetTime 处的取值(取最近的采样点);
单调时间轴用只前进的游标(整段 O(n));平移后越界的采样置 NaN(与预览一致);
新信号样式全部保持默认(gain=1 / absolute=false / 两个镜像=false / offsetY=0 / offsetTime=0)。
- 退化路径:时间轴非单调或时间列缺失时无法按下标推进取样,退回把 offsetTime 带在样式上
(两条路径的显示效果相同)。
四、图例托底统一为 GX 编号徽标同款 + 新增更小字体 uiTiny
- 托底高度 = kLaneBadgeHeight、上下边界与 LaneBadgeRect() 完全一致、圆角 = 高度的一半(胶囊形)、
配色与描边与徽标相同(选中态 accentSoft/accent)、水平内边距 = 胶囊半径。
- 文本改用新增字体 UiFonts::uiTiny(10 逻辑 px):原 uiSmall(12px) 几乎顶满 17px 高的胶囊。
- LegendItem 拆出 plateWidth(托底宽 = 内容宽 + 2×内边距),条目步进 = plateWidth + kLegendItemGap;
kLegendPlateRadius 已删除(半径一律由徽标高度推出)。
五、「系数/偏移」「镜像」二级面板改为向上展开
- PopupMenu::Open(..., bool expandUp = false):true 时 (x, y) 是菜单的左下角、菜单向上展开;
BuildLayout 增加同参数并记录 m_buildExpandUp(RebuildLayout 必须复用,否则数值回写触发的重排
会让面板跳回向下展开)。
- SignalPanel::OpenGainMenu / OpenMirrorMenu:锚点 = (button.X, button.Y - kAdjustMenuGap),
即「面板左下角对准按钮左上角、再略微上去一点」(kAdjustMenuGap = 4 逻辑像素)。
六、验证(全部离屏,不占用用户的鼠标 / 键盘 / 屏幕)
- .preflight/uitest 新增 [AR]:
* 边沿计数 plain / gain=50 / offsetY / mirrorVertical / mirrorHorizontal 逐个与「独立参考实现」
逐位一致;offsetY 用例给出 A/B:按原始采样值算会得到 7/6 个边沿(正是用户报告的缺陷);
* 区间边界:起点从 crossing-1 移到 crossing 恰好少一个上升沿;
* 中心轴:absolute + offsetY 的样式下与参考实现逐位一致(32234 个边沿量级);
* 派生信号:无偏移时全部 128 万采样逐点等于 transform(滤波);带 +10 个平均采样周期的偏移时,
抽样点等于 transform(滤波) 在 time[i] - offsetTime(最近采样)处的取值;样式全默认;
* 图例托底:height == 徽标高度、Y == 徽标 Y、radius == height/2、色块与文本都在托底之内;
* 调整面板:菜单左边界 == 按钮左边界、菜单底 == 按钮顶 - 4*scale、且不覆盖按钮(两个按钮各测一次);
* GX 拖拽让位:±0.6H 各让位 1 格(目标 0 / 3),±0.4H 与恰好 ±0.5H 都不让位(严格「超过」)。
- 同步修正 harness 里两处「旧规则」断言(否则必然 FAIL 并误导后来者):
[AP] 「派生信号继承调整变换」→ 「样式默认 + 采样值按 time[i]-offsetTime 烘焙」;
[AN] 「图例带无托底(网格线可透出)」→ 「装饰矩形逐块、无一块跨越整条带、未被托底覆盖的列
仍能看到底层内容」。
- 合计 722/722 通过;make release 零警告(本机 GCC 15.2.0 UCRT;Target.exe 被占用时链接会报
Permission denied,已用临时 -o 名字验证链接正确性)。
规则同步:Documents/agent_mem.md 新增 §9.21,§9.19 的「offsetTime 仍需继承」标注为已作废,
新增 §11.2 第 21–25 条硬约束,并补变更日志一行。
This commit is contained in:
1 parent
1b3ab4ac09
commit
a2a38236fe
9 files changed
+434
-118
No files matched your search
@@ -1307,8 +1307,12 @@ namespace jdi
|
||||
*
|
||||
* 曲线由「均值点数 X + 迭代次数 N」确定,与「实验性功能 → 均值滤波」的预览曲线逐位一致。
|
||||
*
|
||||
* ⚠️ 判定对象是**画出来的两条曲线**:信号与中心曲线都要先按 `style` 做水平镜像映射、
|
||||
* 再套用 `ApplyDrawTransform`(系数 → 绝对值 → 垂直镜像 → Y 偏移)。
|
||||
*
|
||||
* @param [IN] file 信号所属文件。
|
||||
* @param [IN] signal 目标信号。
|
||||
* @param [IN] style 该信号的外观(含水平镜像与「调整」变换)。
|
||||
* @param [IN] points 均值点数 X。
|
||||
* @param [IN] iterations 迭代次数 N。
|
||||
* @param [IN] hasRange 是否只统计某个时间区间(双光标模式下为光标区间)。
|
||||
@@ -1317,9 +1321,9 @@ namespace jdi
|
||||
* @param [OUT] rising 上升沿数量(信号从下往上越过均值滤波曲线)。
|
||||
* @param [OUT] falling 下降沿数量(信号从上往下穿过均值滤波曲线)。
|
||||
*/
|
||||
void EdgeCenterCountsFor(const DataFile& file, const SignalData& signal, int points, int iterations,
|
||||
bool hasRange, double rangeMin, double rangeMax, std::size_t& rising,
|
||||
std::size_t& falling) const;
|
||||
void EdgeCenterCountsFor(const DataFile& file, const SignalData& signal, const SignalStyle& style,
|
||||
int points, int iterations, bool hasRange, double rangeMin,
|
||||
double rangeMax, std::size_t& rising, std::size_t& falling) const;
|
||||
|
||||
/**
|
||||
* @brief 切换某个信号在「中心轴边沿计数」面板中的勾选状态。
|
||||
@@ -1591,8 +1595,19 @@ namespace jdi
|
||||
|
||||
/**
|
||||
* @brief 统计某信号穿越横向光标幅值的上/下边沿数量,结果带缓存。
|
||||
*
|
||||
* 判定定义(用户明确要求):把信号**绘制出来的曲线**与横向光标所在的幅值比较,
|
||||
* 曲线从**下往上**穿过该幅值 → 上升沿;从**上往下**穿过 → 下降沿。
|
||||
*
|
||||
* ⚠️ 因此判定值必须是**画出来的值**:先按 `style` 做水平镜像映射,再套用
|
||||
* `ApplyDrawTransform`(系数 → 绝对值 → 垂直镜像 → Y 偏移)。
|
||||
* 只比较原始采样值会导致「应用过调整的信号数不到任何边沿」
|
||||
*(横向光标读的是屏幕上的值,与原始值域完全不同)。
|
||||
* ⚠️ 时间区间**之外**的采样一律不参与计数:只有两个端点都在区间内的采样对才计入。
|
||||
*
|
||||
* @param [IN] file 信号所属文件。
|
||||
* @param [IN] signal 目标信号。
|
||||
* @param [IN] style 该信号的外观(含水平镜像与「调整」变换)。
|
||||
* @param [IN] edgeValue 横向光标所在的幅值。
|
||||
* @param [IN] hasRange 是否只统计某个时间区间(双光标模式下为光标区间)。
|
||||
* @param [IN] rangeMin 区间左端点时间。
|
||||
@@ -1600,9 +1615,9 @@ namespace jdi
|
||||
* @param [OUT] rising 上升沿数量(从下往上穿越)。
|
||||
* @param [OUT] falling 下降沿数量(从上往下穿越)。
|
||||
*/
|
||||
void EdgeCountsFor(const DataFile& file, const SignalData& signal, double edgeValue,
|
||||
bool hasRange, double rangeMin, double rangeMax, std::size_t& rising,
|
||||
std::size_t& falling) const;
|
||||
void EdgeCountsFor(const DataFile& file, const SignalData& signal, const SignalStyle& style,
|
||||
double edgeValue, bool hasRange, double rangeMin, double rangeMax,
|
||||
std::size_t& rising, std::size_t& falling) const;
|
||||
|
||||
/**
|
||||
* @brief 计算实验性功能面板所需的宽度(排版与绘制共用,保证文字完整不被省略)。
|
||||
@@ -1816,6 +1831,7 @@ namespace jdi
|
||||
{
|
||||
const DataFile* file = nullptr; /**< 所属数据分组。 */
|
||||
const SignalData* signal = nullptr; /**< 目标信号。 */
|
||||
std::uint64_t styleKey = 0; /**< 「调整」变换 + 水平镜像的指纹(变了必须整段重算)。 */
|
||||
double edgeValue = 0.0; /**< 判定阈值(横向光标幅值)。 */
|
||||
bool hasRange = false; /**< 是否限定在时间区间内统计。 */
|
||||
bool reusable = false; /**< 计数是否完全由 [begin, end) 决定。 */
|
||||
@@ -1867,6 +1883,7 @@ namespace jdi
|
||||
{
|
||||
const DataFile* file = nullptr; /**< 所属数据分组。 */
|
||||
const SignalData* signal = nullptr; /**< 目标信号。 */
|
||||
std::uint64_t styleKey = 0; /**< 「调整」变换 + 水平镜像的指纹(变了必须整段重算)。 */
|
||||
int points = 5; /**< 均值点数 X。 */
|
||||
int iterations = 1; /**< 迭代次数 N。 */
|
||||
bool hasRange = false; /**< 是否限定在时间区间内统计。 */
|
||||
|
||||
Reference in new issue
Block a user