Files
BadNote/lib/editor/canvas/input_diagnostics.dart
Akiba So 85af037b7d
All checks were successful
CI / Windows build (push) Successful in 9m55s
fix: coalesce pinch updates and stop live zoom write-back
Surface Aug6 diag showed sDrop=0 but ~220 same-ms dual ZOOM frames and √2 cur ping-pong from reading currentZoom back into pinch state. Flush once per microtask and embed gitSha in diagnostic meta.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-06 03:44:10 +08:00

108 lines
4.1 KiB
Dart

// lib/editor/canvas/input_diagnostics.dart
//
// Live zoom/pan diagnostics for the pen canvas. PenInteractiveViewer records one
// entry per scale-update frame; the editor's diagnostic overlay displays the
// accumulated summary + a rolling trace so a SINGLE device session reveals the
// nature of any "跳变" (is it a raw-scale spike, a focal/position jump, or a
// pointer-count oscillation?). All numbers reset via [reset].
import 'package:flutter/foundation.dart';
import '../../diagnostics/frame_sampler.dart';
import '../input/diagnostic_logger.dart';
class InputDiagnostics extends ChangeNotifier {
InputDiagnostics._();
static final InputDiagnostics instance = InputDiagnostics._();
int frames = 0;
int scaleDropped = 0; // frames HARD-rejected (legacy; prefer soft-clamp)
int softClamped = 0; // frames whose step was soft-clamped (still applied)
int focalDropped = 0; // frames rejected as a focal/position glitch
int rebaselines = 0; // pointer-count re-baselines
int pointerCountMax = 0;
double rawScaleMin = double.infinity, rawScaleMax = 0;
double scaleMin = double.infinity, scaleMax = 0;
double maxFocalJumpPx = 0; // largest single-frame local focal delta
double maxAppliedScaleJump = 1; // largest single-frame applied scale ratio
final List<String> _trace = <String>[];
List<String> get trace => List.unmodifiable(_trace);
void recordRebaseline() {
rebaselines++;
DiagnosticLogger.instance.log('ZOOM rebaseline');
notifyListeners();
}
void recordScaleFrame({
required double rawScale,
required int pointerCount,
required double currentScale,
required double appliedChange, // 1.0 when the frame was dropped
required double focalJumpPx,
required bool scaleDrop,
required bool focalDrop,
bool softClamped = false,
double? liveScale,
}) {
frames++;
if (scaleDrop) scaleDropped++;
if (softClamped) this.softClamped++;
if (focalDrop) focalDropped++;
if (rawScale < rawScaleMin) rawScaleMin = rawScale;
if (rawScale > rawScaleMax) rawScaleMax = rawScale;
final double resulting = currentScale;
if (resulting < scaleMin) scaleMin = resulting;
if (resulting > scaleMax) scaleMax = resulting;
if (pointerCount > pointerCountMax) pointerCountMax = pointerCount;
if (focalJumpPx > maxFocalJumpPx) maxFocalJumpPx = focalJumpPx;
final double jump = appliedChange >= 1 ? appliedChange : 1 / appliedChange;
if (jump > maxAppliedScaleJump) maxAppliedScaleJump = jump;
final liveBit = liveScale == null
? ''
: ' live=${liveScale.toStringAsFixed(3)}';
final String line = 'p$pointerCount raw=${rawScale.toStringAsFixed(3)} '
'ch=${appliedChange.toStringAsFixed(3)} '
'cur=${currentScale.toStringAsFixed(3)}$liveBit '
'fj=${focalJumpPx.toStringAsFixed(0)}'
'${softClamped ? " SCLAMP" : ""}'
'${scaleDrop ? " SDROP" : ""}'
'${focalDrop ? " FDROP" : ""}';
_trace.add(line);
if (_trace.length > 24) _trace.removeAt(0);
FrameSampler.instance.recordZoom(
rawScale: rawScale,
scaleDrop: scaleDrop || softClamped,
focalDrop: focalDrop,
focalJumpPx: focalJumpPx,
);
DiagnosticLogger.instance.log('ZOOM $line');
notifyListeners();
}
void reset() {
frames = scaleDropped = softClamped = focalDropped = rebaselines =
pointerCountMax = 0;
rawScaleMin = scaleMin = double.infinity;
rawScaleMax = scaleMax = 0;
maxFocalJumpPx = 0;
maxAppliedScaleJump = 1;
_trace.clear();
notifyListeners();
}
String summary() {
final rawLo = rawScaleMin.isFinite ? rawScaleMin.toStringAsFixed(2) : '-';
final rawHi = rawScaleMax > 0 ? rawScaleMax.toStringAsFixed(2) : '-';
final scLo = scaleMin.isFinite ? scaleMin.toStringAsFixed(2) : '-';
final scHi = scaleMax > 0 ? scaleMax.toStringAsFixed(2) : '-';
return 'zoom f=$frames sDrop=$scaleDropped sClamp=$softClamped '
'fDrop=$focalDropped rebase=$rebaselines pMax=$pointerCountMax\n'
' raw=$rawLo..$rawHi scale=$scLo..$scHi\n'
' maxFocalJump=${maxFocalJumpPx.toStringAsFixed(0)}px '
'maxScaleJump=${maxAppliedScaleJump.toStringAsFixed(2)}';
}
}