fix: soft-clamp pinch zoom and Krita-inspired brush opacity
All checks were successful
CI / Windows build (push) Successful in 10m28s

Hard SDROP avalanches froze lastRaw while zoom still crawled; soft-clamp
and re-anchor instead. Ballpoint is near-solid, pencil uses soft √p without
multiply stacking; PDF ink falls back to nearest page during zoom settle.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-08-05 20:51:15 +08:00
parent 2b1c6ba7e0
commit 4f6fb69dee
7 changed files with 267 additions and 136 deletions

View File

@@ -61,4 +61,62 @@ void main() {
test('degenerate baseline (0) is treated as no cumulative change', () {
expect(solve(2.0, 0.0, 5.0), closeTo(2.0, 1e-9));
});
group('softClampedPinchStep', () {
test('normal step passes through unchanged', () {
final s = softClampedPinchStep(
scaleStart: 1.0,
rawScaleAtBaseline: 1.0,
rawScale: 1.1,
lastAppliedScale: 1.0,
minScale: min,
maxScale: max,
maxStepRatio: 1.18,
);
expect(s.appliedScale, closeTo(1.1, 1e-9));
expect(s.spiked, isFalse);
expect(s.reanchor, isFalse);
});
test('spike is soft-clamped and requests reanchor (no hard drop)', () {
// Ideal would jump 1.0 → 2.0 (ratio 2.0 >> 1.18).
final s = softClampedPinchStep(
scaleStart: 1.0,
rawScaleAtBaseline: 1.0,
rawScale: 2.0,
lastAppliedScale: 1.0,
minScale: min,
maxScale: max,
maxStepRatio: 1.18,
);
expect(s.appliedScale, closeTo(1.18, 1e-9));
expect(s.spiked, isTrue);
expect(s.reanchor, isTrue);
});
test('after reanchor, next frame can continue smoothly', () {
final spike = softClampedPinchStep(
scaleStart: 1.0,
rawScaleAtBaseline: 1.0,
rawScale: 2.0,
lastAppliedScale: 1.0,
minScale: min,
maxScale: max,
maxStepRatio: 1.18,
);
// Caller re-anchors: scaleStart=1.18, baseline=2.0, last=1.18.
// Continue toward ideal 2.0 with raw still 2.0 → applied stays 1.18.
final next = softClampedPinchStep(
scaleStart: spike.appliedScale,
rawScaleAtBaseline: 2.0,
rawScale: 2.0,
lastAppliedScale: spike.appliedScale,
minScale: min,
maxScale: max,
maxStepRatio: 1.18,
);
expect(next.appliedScale, closeTo(1.18, 1e-9));
expect(next.spiked, isFalse);
});
});
}