288 信号卡顿根治 + 中心轴面板重做 + 标签页默认状态
一、卡顿真因(实测 29.8ms/帧 → 6.6ms/帧) 1. 中心轴面板高度没有上限:288 个信号时面板高 9996px(绘图区只有 1141px) ⇒ PanelRectInsideLayer 恒为假 ⇒ 面板像素缓存一次都存不进去 ⇒ 每帧全量重画。 2. PanelGlobalKey 混入鼠标驱动状态(m_hoverSignal / m_hoverLane / m_hoverCursor / CursorTime / EdgeRangeMin/Max)⇒ 鼠标一动键就变 ⇒ 所有面板每帧重画; PanelSlotKey 里还会为每个信号调一次 EdgeCenterCountsFor(单帧 288 次查询)。 3. 缓存命中路径的投影(与面板同大的半透明抗锯齿圆角矩形)每帧重画,GDI+ 实测 > 20ms。 修法: * 新增 PlotView::InputIsInteractive()(拖拽中 / 鼠标 140ms 内移动过)。交互期间冻结 全部鼠标驱动项与统计数字、跳过投影、统计预算降到 8 万(静止时 40 万)。 m_meanRowHoverLane/Ref 例外 —— 它直接改面板像素且只在进出某行时变化。 * kEdgeScanPairsPerFrame 由 1,200,000 降为 400,000。 * 中心轴面板重做为可滚动列表 ⇒ 面板高度被绘图区夹住、缓存可用、只画可见行。 二、「中心轴边沿计数」面板重做(与「均值滤波」同风格) 条目栏为信号色叠底的可折叠栏 + 右端复选框;栏下同一行显示「上升沿 / 下降沿」; 再下面两行滑块(均值点数 / 迭代次数,标签不带 X/N),数值可点击输入上限、 右侧复位按钮;整个面板可滚动;未勾选时条目栏不可展开。 新增 Lane::edgeCenterScroll + SetLaneEdgeCenterScroll;交互复用 MEAN_* 拖拽模式 + m_meanDragCenter / m_editMeanCenter 标志分派;光标形状与均值滤波一致。 三、标签页 * 唯一标签页点 ✕ 不再弹警告:直接 erase 后 NewTab(true),名字必然为「视图 1」。 * 新建标签页(含手动新建)默认:组全部折起、无选中信号(清空全局 Selected)、 样式区无目标。 四、验证 .preflight/bugcheck.exe 59/59(A1 空闲 0.7ms、A2 平移 2.5ms、A3 双光标 2.4ms、 A4 自由标记 5.2ms、A5 中心轴拖动 6.6ms;B3 真实 WM_TIMER+WM_PAINT 路径; B5 快速连点与动画反向;C 中心轴面板交互;D 标签页默认状态) panelcheck 71/71、markercheck 13/13、ringcheck 6/6 make release / make debug 零 warning
This commit is contained in:
1 parent
8271879ede
commit
944c074ab3
11 files changed
+618
-122
No files matched your search
@@ -651,19 +651,25 @@ namespace jdi
|
||||
target == PlotEditTarget::MEAN_ITERATIONS_MAX)
|
||||
{
|
||||
Lane& lane = m_tab->Lanes()[static_cast<std::size_t>(laneIndex)];
|
||||
if (lane.FindMeanFilterItem(m_editMeanRef) == nullptr)
|
||||
const Lane::MeanFilterItem* existing = m_editMeanCenter
|
||||
? lane.FindEdgeCenterItem(m_editMeanRef)
|
||||
: lane.FindMeanFilterItem(m_editMeanRef);
|
||||
if (existing == nullptr)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (parsedOk)
|
||||
{
|
||||
const bool isPoints = (target == PlotEditTarget::MEAN_POINTS_MAX);
|
||||
const int minimum = isPoints ? kMeanFilterPointsMin : kMeanFilterIterationsMin;
|
||||
const int minimum =
|
||||
isPoints ? (m_editMeanCenter ? 0 : kMeanFilterPointsMin) : kMeanFilterIterationsMin;
|
||||
const int limit = isPoints ? kMeanFilterPointsMaxLimit : kMeanFilterIterationsMaxLimit;
|
||||
const long long value = static_cast<long long>(std::llround(parsed));
|
||||
if (value >= minimum && value <= limit)
|
||||
{
|
||||
Lane::MeanFilterItem& item = lane.EnsureMeanFilterItem(m_editMeanRef);
|
||||
Lane::MeanFilterItem& item = m_editMeanCenter
|
||||
? lane.EnsureEdgeCenterItem(m_editMeanRef)
|
||||
: lane.EnsureMeanFilterItem(m_editMeanRef);
|
||||
if (isPoints)
|
||||
{
|
||||
item.maxPoints = static_cast<int>(value);
|
||||
@@ -676,6 +682,10 @@ namespace jdi
|
||||
std::max(minimum, std::min(item.maxIterations, item.iterations));
|
||||
}
|
||||
m_meanCurveCache.clear();
|
||||
if (m_editMeanCenter)
|
||||
{
|
||||
InvalidateEdgeScansFor(ResolveSignal(m_editMeanRef).data, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
@@ -1060,6 +1070,67 @@ namespace jdi
|
||||
}
|
||||
}
|
||||
}
|
||||
// 「均值滤波 / 中心轴边沿计数」可滚动列表:条目栏(含复选框)→ 手型;
|
||||
// 滑块轨道 → 左右箭头;数值(可点击改上限)与复位按钮 → 手型;
|
||||
// 内容区滚动条 → 上下箭头。与「弱峰调整」同一套观感。
|
||||
if (static_cast<EdgePanelKind>(panelKind) == EdgePanelKind::MEAN ||
|
||||
static_cast<EdgePanelKind>(panelKind) == EdgePanelKind::CENTER)
|
||||
{
|
||||
for (std::size_t rowIndex = 0; rowIndex < layout.rowHeaders.size(); ++rowIndex)
|
||||
{
|
||||
if (layout.rowHeaders[rowIndex].Width > 0.0f &&
|
||||
UiPointInRect(layout.rowHeaders[rowIndex], static_cast<float>(x),
|
||||
static_cast<float>(y)))
|
||||
{
|
||||
return LoadCursorW(nullptr, IDC_HAND);
|
||||
}
|
||||
if (rowIndex < layout.rowLevelValues.size() &&
|
||||
layout.rowLevelValues[rowIndex].Width > 0.0f &&
|
||||
UiPointInRect(layout.rowLevelValues[rowIndex], static_cast<float>(x),
|
||||
static_cast<float>(y)))
|
||||
{
|
||||
return LoadCursorW(nullptr, IDC_HAND);
|
||||
}
|
||||
if (rowIndex < layout.rowIterValues.size() &&
|
||||
layout.rowIterValues[rowIndex].Width > 0.0f &&
|
||||
UiPointInRect(layout.rowIterValues[rowIndex], static_cast<float>(x),
|
||||
static_cast<float>(y)))
|
||||
{
|
||||
return LoadCursorW(nullptr, IDC_HAND);
|
||||
}
|
||||
if (rowIndex < layout.rowLevelResets.size() &&
|
||||
layout.rowLevelResets[rowIndex].Width > 0.0f &&
|
||||
UiPointInRect(layout.rowLevelResets[rowIndex], static_cast<float>(x),
|
||||
static_cast<float>(y)))
|
||||
{
|
||||
return LoadCursorW(nullptr, IDC_HAND);
|
||||
}
|
||||
if (rowIndex < layout.rowIterResets.size() &&
|
||||
layout.rowIterResets[rowIndex].Width > 0.0f &&
|
||||
UiPointInRect(layout.rowIterResets[rowIndex], static_cast<float>(x),
|
||||
static_cast<float>(y)))
|
||||
{
|
||||
return LoadCursorW(nullptr, IDC_HAND);
|
||||
}
|
||||
if (rowIndex < layout.rowLevelTracks.size() &&
|
||||
WeakPeakSliderHit(layout.rowLevelTracks[rowIndex], m_scale,
|
||||
static_cast<float>(x), static_cast<float>(y)))
|
||||
{
|
||||
return LoadCursorW(nullptr, IDC_SIZEWE);
|
||||
}
|
||||
if (rowIndex < layout.rowIterTracks.size() &&
|
||||
WeakPeakSliderHit(layout.rowIterTracks[rowIndex], m_scale,
|
||||
static_cast<float>(x), static_cast<float>(y)))
|
||||
{
|
||||
return LoadCursorW(nullptr, IDC_SIZEWE);
|
||||
}
|
||||
}
|
||||
if (layout.scrollThumb.Width > 0.0f &&
|
||||
UiPointInRect(layout.scrollThumb, static_cast<float>(x), static_cast<float>(y)))
|
||||
{
|
||||
return LoadCursorW(nullptr, IDC_SIZENS);
|
||||
}
|
||||
}
|
||||
// 「弱峰调整」的逐信号滑块:轨道 → 左右箭头;迭代次数右侧数值 → 手型(可点击改上限)。
|
||||
if (static_cast<EdgePanelKind>(panelKind) == EdgePanelKind::PEAK)
|
||||
{
|
||||
@@ -1164,11 +1235,32 @@ namespace jdi
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool PlotView::InputIsInteractive() const
|
||||
{
|
||||
if (m_dragMode != PlotDragMode::NONE)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (m_lastInputActivityMs == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
const std::uint64_t now = GetTickCount64();
|
||||
return (now >= m_lastInputActivityMs) &&
|
||||
((now - m_lastInputActivityMs) < kEdgeScanInteractiveIdleMs);
|
||||
}
|
||||
|
||||
bool PlotView::OnMouseMove(int x, int y)
|
||||
{
|
||||
const bool mouseMoved = (x != m_mouseX) || (y != m_mouseY);
|
||||
m_mouseX = x;
|
||||
m_mouseY = y;
|
||||
// 交互活动时间戳:`Paint` 用它决定边沿统计本帧用「交互预算」还是「静止预算」。
|
||||
// 只有真正的移动才更新 —— 悬停不动时统计应当全速推进。
|
||||
if (mouseMoved)
|
||||
{
|
||||
m_lastInputActivityMs = GetTickCount64();
|
||||
}
|
||||
|
||||
if (!HasDrawableContent())
|
||||
{
|
||||
@@ -1441,21 +1533,26 @@ namespace jdi
|
||||
case PlotDragMode::MEAN_POINTS:
|
||||
case PlotDragMode::MEAN_ITERATIONS:
|
||||
{
|
||||
// 「均值滤波」某信号的参数滑块:量程 = 下限 … 该信号自己的上限。
|
||||
// 「均值滤波 / 中心轴边沿计数」某信号的参数滑块:量程 = 下限 … 该信号自己的上限。
|
||||
if (m_tab == nullptr || m_meanDragLane < 0 ||
|
||||
m_meanDragLane >= static_cast<int>(m_tab->Lanes().size()))
|
||||
{
|
||||
break;
|
||||
}
|
||||
Lane& lane = m_tab->Lanes()[static_cast<std::size_t>(m_meanDragLane)];
|
||||
if (lane.FindMeanFilterItem(m_meanDragRef) == nullptr)
|
||||
const Lane::MeanFilterItem* existingItem = m_meanDragCenter
|
||||
? lane.FindEdgeCenterItem(m_meanDragRef)
|
||||
: lane.FindMeanFilterItem(m_meanDragRef);
|
||||
if (existingItem == nullptr)
|
||||
{
|
||||
break;
|
||||
}
|
||||
Lane::MeanFilterItem& item = lane.EnsureMeanFilterItem(m_meanDragRef);
|
||||
Lane::MeanFilterItem& item = m_meanDragCenter
|
||||
? lane.EnsureEdgeCenterItem(m_meanDragRef)
|
||||
: lane.EnsureMeanFilterItem(m_meanDragRef);
|
||||
const bool pointsMode = (m_dragMode == PlotDragMode::MEAN_POINTS);
|
||||
const EdgePanelLayout layout =
|
||||
EdgePanelLayoutFor(m_meanDragLane, EdgePanelKind::MEAN);
|
||||
const EdgePanelLayout layout = EdgePanelLayoutFor(
|
||||
m_meanDragLane, m_meanDragCenter ? EdgePanelKind::CENTER : EdgePanelKind::MEAN);
|
||||
int rowIndex = -1;
|
||||
for (std::size_t candidate = 0; candidate < layout.rowRefs.size(); ++candidate)
|
||||
{
|
||||
@@ -1480,13 +1577,15 @@ namespace jdi
|
||||
UiSliderRatioFromX(tracks[static_cast<std::size_t>(rowIndex)], fx);
|
||||
if (pointsMode)
|
||||
{
|
||||
const int maximum = std::max(
|
||||
kMeanFilterPointsMin, std::min(kMeanFilterPointsMaxLimit, item.maxPoints));
|
||||
item.points = std::max(
|
||||
kMeanFilterPointsMin,
|
||||
std::min(maximum,
|
||||
kMeanFilterPointsMin +
|
||||
static_cast<int>(std::lround(ratio * (maximum - kMeanFilterPointsMin)))));
|
||||
// ⚠️ 下限:均值滤波 = 1(用户 2026-10-30 要求不能为 0);中心轴 = 0(0 = 不平滑)。
|
||||
const int floorValue = m_meanDragCenter ? 0 : kMeanFilterPointsMin;
|
||||
const int maximum =
|
||||
std::max(floorValue, std::min(kMeanFilterPointsMaxLimit, item.maxPoints));
|
||||
item.points =
|
||||
std::max(floorValue,
|
||||
std::min(maximum,
|
||||
floorValue +
|
||||
static_cast<int>(std::lround(ratio * (maximum - floorValue)))));
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1501,6 +1600,11 @@ namespace jdi
|
||||
ratio * (maximum - kMeanFilterIterationsMin)))));
|
||||
}
|
||||
m_meanCurveCache.clear();
|
||||
// 中心轴统计依赖参数 ⇒ 参数一变必须定向作废该信号的累积状态。
|
||||
if (m_meanDragCenter)
|
||||
{
|
||||
InvalidateEdgeScansFor(ResolveSignal(m_meanDragRef).data, true);
|
||||
}
|
||||
m_dragMoved = true;
|
||||
return true;
|
||||
}
|
||||
@@ -1639,14 +1743,14 @@ namespace jdi
|
||||
}
|
||||
case PlotDragMode::MEAN_SCROLL:
|
||||
{
|
||||
// 「均值滤波」面板内容滚动条:按滑块在轨道里的位置换算滚动偏移。
|
||||
// 「均值滤波 / 中心轴边沿计数」面板内容滚动条:按滑块在轨道里的位置换算滚动偏移。
|
||||
if (m_tab == nullptr || m_markerScrollDragLane < 0 ||
|
||||
m_markerScrollDragLane >= static_cast<int>(m_tab->Lanes().size()))
|
||||
{
|
||||
break;
|
||||
}
|
||||
const EdgePanelLayout layout =
|
||||
EdgePanelLayoutFor(m_markerScrollDragLane, EdgePanelKind::MEAN);
|
||||
const EdgePanelLayout layout = EdgePanelLayoutFor(
|
||||
m_markerScrollDragLane, m_meanDragCenter ? EdgePanelKind::CENTER : EdgePanelKind::MEAN);
|
||||
const float travel = layout.scrollTrack.Height - layout.scrollThumb.Height;
|
||||
const float maxScroll =
|
||||
std::max(0.0f, layout.markerContentHeight - layout.content.Height);
|
||||
@@ -1659,7 +1763,11 @@ namespace jdi
|
||||
std::min(static_cast<float>(y) - m_markerScrollGrabOffset,
|
||||
layout.scrollTrack.Y + travel));
|
||||
const float ratio = (thumbTop - layout.scrollTrack.Y) / travel;
|
||||
if (SetLaneMeanFilterScroll(m_markerScrollDragLane, ratio * maxScroll))
|
||||
const bool scrolled =
|
||||
m_meanDragCenter
|
||||
? SetLaneEdgeCenterScroll(m_markerScrollDragLane, ratio * maxScroll)
|
||||
: SetLaneMeanFilterScroll(m_markerScrollDragLane, ratio * maxScroll);
|
||||
if (scrolled)
|
||||
{
|
||||
m_dragMoved = true;
|
||||
return true;
|
||||
@@ -1827,17 +1935,18 @@ namespace jdi
|
||||
hoverPanelKind);
|
||||
}
|
||||
|
||||
// 「均值滤波 / 弱峰调整」面板**条目栏**的悬停 → 高亮该信号的预览曲线
|
||||
//(用户 2026-10-30 要求:"鼠标处于条目栏上方时,对应的预览曲线需要高亮提示")。
|
||||
// 「均值滤波 / 弱峰调整 / 中心轴边沿计数」面板**条目栏**的悬停 → 高亮该条目
|
||||
//(用户 2026-10-30 要求:"鼠标处于条目栏上方时,对应的预览曲线需要高亮提示";
|
||||
// 中心轴边沿计数自 2026-11-02 起同样有条目栏,条目本身也会叠一层强调色)。
|
||||
{
|
||||
int hoverLane = -1;
|
||||
SignalRef hoverRef;
|
||||
const EdgePanelKind hoverKind = static_cast<EdgePanelKind>(hoverPanelKind);
|
||||
if (overEdgePanel && m_tab != nullptr && panelLane >= 0 &&
|
||||
(static_cast<EdgePanelKind>(hoverPanelKind) == EdgePanelKind::MEAN ||
|
||||
static_cast<EdgePanelKind>(hoverPanelKind) == EdgePanelKind::PEAK))
|
||||
(hoverKind == EdgePanelKind::MEAN || hoverKind == EdgePanelKind::PEAK ||
|
||||
hoverKind == EdgePanelKind::CENTER))
|
||||
{
|
||||
const EdgePanelLayout layout =
|
||||
EdgePanelLayoutFor(panelLane, static_cast<EdgePanelKind>(hoverPanelKind));
|
||||
const EdgePanelLayout layout = EdgePanelLayoutFor(panelLane, hoverKind);
|
||||
if (UiPointInRect(layout.content, static_cast<float>(x), static_cast<float>(y)))
|
||||
{
|
||||
for (std::size_t rowIndex = 0; rowIndex < layout.rowRefs.size(); ++rowIndex)
|
||||
@@ -2236,14 +2345,29 @@ namespace jdi
|
||||
m_tab != nullptr)
|
||||
{
|
||||
const bool meanMode = (kind == EdgePanelKind::MEAN);
|
||||
const bool centerMode = (kind == EdgePanelKind::CENTER);
|
||||
const bool boostMode = (kind == EdgePanelKind::PEAK);
|
||||
// 「均值滤波」与「中心轴边沿计数」自 2026-11-02 起共用同一套**可滚动列表**交互
|
||||
//(信号色叠底条目栏 + 右侧复选框 + 展开后的两行滑块)。
|
||||
const bool listMode = (meanMode || centerMode);
|
||||
// 「均值滤波」:组头三角(展开 / 收起参数行)+ 两行参数滑块
|
||||
//(轨道拖动 / 点击数值改上限 / 右侧复位按钮)。
|
||||
// ⚠️ 所有命中都要求落在**内容区**之内:滚动到可视范围之外的行仍保留矩形,
|
||||
// 若不加这条限制,它们会抢走底部「构建为新信号」按钮的点击。
|
||||
if (meanMode)
|
||||
if (listMode)
|
||||
{
|
||||
Lane& meanLane = m_tab->Lanes()[static_cast<std::size_t>(index)];
|
||||
// 本面板的条目查找 / 创建(两者共用 `Lane::MeanFilterItem`)。
|
||||
const auto findItem = [&meanLane, centerMode](const SignalRef& ref)
|
||||
-> const Lane::MeanFilterItem* {
|
||||
return centerMode ? meanLane.FindEdgeCenterItem(ref)
|
||||
: meanLane.FindMeanFilterItem(ref);
|
||||
};
|
||||
const auto ensureItem = [&meanLane, centerMode](const SignalRef& ref)
|
||||
-> Lane::MeanFilterItem& {
|
||||
return centerMode ? meanLane.EnsureEdgeCenterItem(ref)
|
||||
: meanLane.EnsureMeanFilterItem(ref);
|
||||
};
|
||||
// 内容区右侧的滚动条滑块:按下即进入拖动(与「自由标记」面板同一交互)。
|
||||
if (layout.scrollThumb.Width > 0.0f &&
|
||||
UiPointInRect(layout.scrollThumb, fx, fy))
|
||||
@@ -2251,6 +2375,7 @@ namespace jdi
|
||||
m_dragMode = PlotDragMode::MEAN_SCROLL;
|
||||
m_markerScrollDragLane = index;
|
||||
m_markerScrollGrabOffset = fy - layout.scrollThumb.Y;
|
||||
m_meanDragCenter = centerMode;
|
||||
return true;
|
||||
}
|
||||
const bool insideContent =
|
||||
@@ -2267,10 +2392,10 @@ namespace jdi
|
||||
const bool hasParams =
|
||||
rowIndex < layout.rowLevelTracks.size() &&
|
||||
layout.rowLevelTracks[rowIndex].Width > 0.0f &&
|
||||
meanLane.FindMeanFilterItem(ref) != nullptr;
|
||||
findItem(ref) != nullptr;
|
||||
if (hasParams)
|
||||
{
|
||||
Lane::MeanFilterItem& item = meanLane.EnsureMeanFilterItem(ref);
|
||||
Lane::MeanFilterItem& item = ensureItem(ref);
|
||||
// 复位:均值点数 → 默认值(同时把滑块上限恢复为默认上限)。
|
||||
if (rowIndex < layout.rowLevelResets.size() &&
|
||||
layout.rowLevelResets[rowIndex].Width > 0.0f &&
|
||||
@@ -2279,6 +2404,10 @@ namespace jdi
|
||||
item.points = kMeanFilterPointsDefault;
|
||||
item.maxPoints = kMeanFilterPointsDefaultMax;
|
||||
m_meanCurveCache.clear();
|
||||
if (centerMode)
|
||||
{
|
||||
InvalidateEdgeScansFor(ResolveSignal(ref).data, true);
|
||||
}
|
||||
m_dragMode = PlotDragMode::NONE;
|
||||
return true;
|
||||
}
|
||||
@@ -2290,6 +2419,10 @@ namespace jdi
|
||||
item.iterations = kMeanFilterIterationsMin;
|
||||
item.maxIterations = kMeanFilterIterationsDefaultMax;
|
||||
m_meanCurveCache.clear();
|
||||
if (centerMode)
|
||||
{
|
||||
InvalidateEdgeScansFor(ResolveSignal(ref).data, true);
|
||||
}
|
||||
m_dragMode = PlotDragMode::NONE;
|
||||
return true;
|
||||
}
|
||||
@@ -2300,6 +2433,7 @@ namespace jdi
|
||||
{
|
||||
m_editMeanRef = ref;
|
||||
m_meanEditCurrentMax = item.maxPoints;
|
||||
m_editMeanCenter = centerMode;
|
||||
const gd::RectF& track = layout.rowLevelTracks[rowIndex];
|
||||
const gd::RectF& value = layout.rowLevelValues[rowIndex];
|
||||
const gd::RectF rowRect(track.X, value.Y,
|
||||
@@ -2317,6 +2451,7 @@ namespace jdi
|
||||
{
|
||||
m_editMeanRef = ref;
|
||||
m_meanEditCurrentMax = item.maxIterations;
|
||||
m_editMeanCenter = centerMode;
|
||||
const gd::RectF& track = layout.rowIterTracks[rowIndex];
|
||||
const gd::RectF& value = layout.rowIterValues[rowIndex];
|
||||
const gd::RectF rowRect(track.X, value.Y,
|
||||
@@ -2331,21 +2466,23 @@ namespace jdi
|
||||
if (rowIndex < layout.rowLevelTracks.size() &&
|
||||
WeakPeakSliderHit(layout.rowLevelTracks[rowIndex], m_scale, fx, fy))
|
||||
{
|
||||
const int maximum = std::max(
|
||||
kMeanFilterPointsMin,
|
||||
std::min(kMeanFilterPointsMaxLimit, item.maxPoints));
|
||||
const int floorValue = centerMode ? 0 : kMeanFilterPointsMin;
|
||||
const int maximum =
|
||||
std::max(floorValue, std::min(kMeanFilterPointsMaxLimit, item.maxPoints));
|
||||
m_dragMode = PlotDragMode::MEAN_POINTS;
|
||||
m_meanDragLane = index;
|
||||
m_meanDragRef = ref;
|
||||
m_meanDragCenter = centerMode;
|
||||
const double ratio = UiSliderRatioFromX(layout.rowLevelTracks[rowIndex], fx);
|
||||
item.points = std::max(
|
||||
kMeanFilterPointsMin,
|
||||
floorValue,
|
||||
std::min(maximum,
|
||||
kMeanFilterPointsMin +
|
||||
static_cast<int>(std::lround(
|
||||
ratio *
|
||||
(maximum - kMeanFilterPointsMin)))));
|
||||
floorValue + static_cast<int>(std::lround(ratio * (maximum - floorValue)))));
|
||||
m_meanCurveCache.clear();
|
||||
if (centerMode)
|
||||
{
|
||||
InvalidateEdgeScansFor(ResolveSignal(ref).data, true);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
// 拖动「迭代次数」滑块。
|
||||
@@ -2358,15 +2495,19 @@ namespace jdi
|
||||
m_dragMode = PlotDragMode::MEAN_ITERATIONS;
|
||||
m_meanDragLane = index;
|
||||
m_meanDragRef = ref;
|
||||
m_meanDragCenter = centerMode;
|
||||
const double ratio = UiSliderRatioFromX(layout.rowIterTracks[rowIndex], fx);
|
||||
item.iterations = std::max(
|
||||
kMeanFilterIterationsMin,
|
||||
std::min(maximum,
|
||||
kMeanFilterIterationsMin +
|
||||
static_cast<int>(std::lround(
|
||||
ratio *
|
||||
(maximum - kMeanFilterIterationsMin)))));
|
||||
ratio * (maximum - kMeanFilterIterationsMin)))));
|
||||
m_meanCurveCache.clear();
|
||||
if (centerMode)
|
||||
{
|
||||
InvalidateEdgeScansFor(ResolveSignal(ref).data, true);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -2385,11 +2526,18 @@ namespace jdi
|
||||
rowIndex < layout.rowChecks.size() &&
|
||||
layout.rowChecks[rowIndex].Width > 0.0f &&
|
||||
UiPointInRect(layout.rowChecks[rowIndex], fx, fy);
|
||||
Lane::MeanFilterItem* existing = const_cast<Lane::MeanFilterItem*>(
|
||||
meanLane.FindMeanFilterItem(ref));
|
||||
Lane::MeanFilterItem* existing =
|
||||
const_cast<Lane::MeanFilterItem*>(findItem(ref));
|
||||
if (onCheckBox)
|
||||
{
|
||||
ToggleLaneMeanFilterSignal(index, ref);
|
||||
if (centerMode)
|
||||
{
|
||||
ToggleLaneEdgeCenterSignal(index, ref);
|
||||
}
|
||||
else
|
||||
{
|
||||
ToggleLaneMeanFilterSignal(index, ref);
|
||||
}
|
||||
m_meanRowHoverLane = -1;
|
||||
m_meanRowHoverRef = SignalRef();
|
||||
}
|
||||
@@ -3057,9 +3205,10 @@ namespace jdi
|
||||
// 弱峰滑块拖拽结束:清掉目标(避免留下上一轮的信号引用)。
|
||||
m_peakDragLane = -1;
|
||||
m_peakDragRef = SignalRef();
|
||||
// 「均值滤波」参数滑块拖拽结束:同样清掉目标。
|
||||
// 「均值滤波 / 中心轴边沿计数」参数滑块拖拽结束:同样清掉目标。
|
||||
m_meanDragLane = -1;
|
||||
m_meanDragRef = SignalRef();
|
||||
m_meanDragCenter = false;
|
||||
// 「标记透明度」滑块 / 面板内容滚动条拖拽结束。
|
||||
m_markerOpacityDragLane = -1;
|
||||
m_markerScrollDragLane = -1;
|
||||
@@ -3111,10 +3260,11 @@ namespace jdi
|
||||
panelLane, panelKind))
|
||||
{
|
||||
const EdgePanelKind hoveredKind = static_cast<EdgePanelKind>(panelKind);
|
||||
// 「均值滤波」面板:内容区超高时用滚轮翻页(上百个信号合并时必备)。
|
||||
if (hoveredKind == EdgePanelKind::MEAN)
|
||||
// 「均值滤波 / 中心轴边沿计数」面板:内容区超高时用滚轮翻页
|
||||
//(上百个信号合并时必备)。
|
||||
if (hoveredKind == EdgePanelKind::MEAN || hoveredKind == EdgePanelKind::CENTER)
|
||||
{
|
||||
const EdgePanelLayout layout = EdgePanelLayoutFor(panelLane, EdgePanelKind::MEAN);
|
||||
const EdgePanelLayout layout = EdgePanelLayoutFor(panelLane, hoveredKind);
|
||||
if (layout.content.Width > 0.0f &&
|
||||
UiPointInRect(layout.content, static_cast<float>(m_mouseX),
|
||||
static_cast<float>(m_mouseY)))
|
||||
@@ -3123,11 +3273,21 @@ namespace jdi
|
||||
std::max(0.0f, layout.markerContentHeight - layout.content.Height);
|
||||
if (maxScroll > 0.0f)
|
||||
{
|
||||
const float current =
|
||||
Tab().Lanes()[static_cast<std::size_t>(panelLane)].meanFilterScroll;
|
||||
const Lane& wheelLane =
|
||||
Tab().Lanes()[static_cast<std::size_t>(panelLane)];
|
||||
const float current = (hoveredKind == EdgePanelKind::CENTER)
|
||||
? wheelLane.edgeCenterScroll
|
||||
: wheelLane.meanFilterScroll;
|
||||
const float next = current - static_cast<float>(notches * 40.0 * m_scale);
|
||||
SetLaneMeanFilterScroll(panelLane,
|
||||
std::max(0.0f, std::min(next, maxScroll)));
|
||||
const float target = std::max(0.0f, std::min(next, maxScroll));
|
||||
if (hoveredKind == EdgePanelKind::CENTER)
|
||||
{
|
||||
SetLaneEdgeCenterScroll(panelLane, target);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetLaneMeanFilterScroll(panelLane, target);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user