fix: PDF finger ink, chrome UX, OneNote pens, and pen physics
Some checks failed
CI / Windows build (push) Has been cancelled
Some checks failed
CI / Windows build (push) Has been cancelled
Wire finger drawing on PDF without breaking pinch; auto-hide page scrubber and fix bounce; share sticky tools with resize and per-page remember; side-button select; separate pen slots with colors; rnote pressure shapes plus tip-velocity width and lower stroke latency. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -22,11 +22,11 @@ void main() {
|
||||
}
|
||||
});
|
||||
|
||||
test('fountain pen — Pow2 (p²), moderate thinning, no taper, solid', () {
|
||||
test('fountain pen — Pow2 (p²), higher thinning, low streamline, solid', () {
|
||||
final b = brushProfileFor(BrushKind.fountainPen);
|
||||
expect(b.pressureGamma, 2.0); // rnote Pow2 / quadratic
|
||||
expect(b.pfThinning, 0.65);
|
||||
expect(b.pfStreamline, 0.4);
|
||||
expect(b.pfThinning, 0.75);
|
||||
expect(b.pfStreamline, 0.22);
|
||||
expect(b.pfSmoothing, 0.5);
|
||||
expect(b.simulatePressure, isFalse);
|
||||
expect(b.taper, isFalse);
|
||||
@@ -36,11 +36,11 @@ void main() {
|
||||
expect(b.blendMultiply, isFalse);
|
||||
});
|
||||
|
||||
test('ballpoint — linear, near-constant width (thinning 0.15)', () {
|
||||
test('ballpoint — linear, near-constant width (thinning 0.12)', () {
|
||||
final b = brushProfileFor(BrushKind.ballpoint);
|
||||
expect(b.pressureGamma, 1.0); // rnote Linear
|
||||
expect(b.pfThinning, 0.15);
|
||||
expect(b.pfStreamline, 0.55);
|
||||
expect(b.pfThinning, 0.12);
|
||||
expect(b.pfStreamline, 0.35);
|
||||
expect(b.simulatePressure, isFalse);
|
||||
expect(b.taper, isFalse);
|
||||
});
|
||||
@@ -49,7 +49,7 @@ void main() {
|
||||
final b = brushProfileFor(BrushKind.highlighter);
|
||||
expect(b.pressureGamma, 1.0);
|
||||
expect(b.pfThinning, 0.0); // constant width
|
||||
expect(b.pfStreamline, 0.5);
|
||||
expect(b.pfStreamline, 0.3);
|
||||
expect(b.pfSmoothing, 0.4);
|
||||
expect(b.capStart, isFalse); // square ends
|
||||
expect(b.capEnd, isFalse);
|
||||
@@ -61,7 +61,7 @@ void main() {
|
||||
final b = brushProfileFor(BrushKind.pencil);
|
||||
expect(b.pressureGamma, 0.5); // rnote Sqrt / √p
|
||||
expect(b.pfThinning, 0.45);
|
||||
expect(b.pfStreamline, 0.35);
|
||||
expect(b.pfStreamline, 0.25);
|
||||
expect(b.taper, isFalse);
|
||||
expect(b.blendMultiply, isFalse);
|
||||
});
|
||||
|
||||
34
test/pen_physics_test.dart
Normal file
34
test/pen_physics_test.dart
Normal file
@@ -0,0 +1,34 @@
|
||||
// Tests for tipVelocityWidthScale (physical tip model).
|
||||
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
import 'package:badnote/editor/engine/brush.dart';
|
||||
import 'package:badnote/editor/engine/pen_physics.dart';
|
||||
|
||||
void main() {
|
||||
group('tipVelocityWidthScale', () {
|
||||
test('idle speed leaves all brushes at 1.0', () {
|
||||
for (final k in BrushKind.values) {
|
||||
expect(tipVelocityWidthScale(k, 0.0), closeTo(1.0, 1e-9));
|
||||
}
|
||||
});
|
||||
|
||||
test('fountain thins more than ballpoint at the same speed', () {
|
||||
const speed = 2.0; // reference fast handwriting
|
||||
final fountain = tipVelocityWidthScale(BrushKind.fountainPen, speed);
|
||||
final ballpoint = tipVelocityWidthScale(BrushKind.ballpoint, speed);
|
||||
expect(fountain, closeTo(0.85, 1e-9));
|
||||
expect(ballpoint, closeTo(0.98, 1e-9));
|
||||
expect(fountain, lessThan(ballpoint));
|
||||
});
|
||||
|
||||
test('highlighter ignores velocity', () {
|
||||
expect(tipVelocityWidthScale(BrushKind.highlighter, 5.0), 1.0);
|
||||
});
|
||||
|
||||
test('NaN / negative speed treated as idle', () {
|
||||
expect(tipVelocityWidthScale(BrushKind.fountainPen, double.nan), 1.0);
|
||||
expect(tipVelocityWidthScale(BrushKind.fountainPen, -1.0), 1.0);
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
// Tests for the configurable pen-pressure response (F5).
|
||||
|
||||
import 'dart:math' as math;
|
||||
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
import 'package:badnote/editor/input/pressure_curve.dart';
|
||||
@@ -46,4 +48,59 @@ void main() {
|
||||
// Even a light touch stays near full width.
|
||||
expect(marker.apply(0.1), greaterThan(0.9));
|
||||
});
|
||||
|
||||
group('PressureCurve.shaped', () {
|
||||
const floor = 0.1;
|
||||
|
||||
test('every shape maps 0→floor and 1→1', () {
|
||||
for (final shape in PressureCurveShape.values) {
|
||||
final c = PressureCurve.shaped(shape, floor: floor);
|
||||
expect(c.apply(0.0), closeTo(floor, 1e-9), reason: '$shape at 0');
|
||||
expect(c.apply(1.0), closeTo(1.0, 1e-9), reason: '$shape at 1');
|
||||
}
|
||||
});
|
||||
|
||||
test('midpoints differ across shapes', () {
|
||||
final mids = {
|
||||
for (final shape in PressureCurveShape.values)
|
||||
shape: PressureCurve.shaped(shape).apply(0.5),
|
||||
};
|
||||
expect(mids[PressureCurveShape.linear], closeTo(0.5, 1e-9));
|
||||
expect(mids[PressureCurveShape.soft], closeTo(math.pow(0.5, 0.6), 1e-9));
|
||||
expect(mids[PressureCurveShape.quadratic], closeTo(0.25, 1e-9));
|
||||
expect(mids[PressureCurveShape.cubic], closeTo(0.125, 1e-9));
|
||||
expect(mids[PressureCurveShape.sqrt], closeTo(math.sqrt(0.5), 1e-9));
|
||||
final logMid = math.log(1 + kLogarithmicPressureK * 0.5) /
|
||||
math.log(1 + kLogarithmicPressureK);
|
||||
expect(mids[PressureCurveShape.logarithmic], closeTo(logMid, 1e-9));
|
||||
|
||||
// Soft (γ<1) > linear > quadratic > cubic at p=0.5; log is above linear.
|
||||
expect(mids[PressureCurveShape.soft]! > mids[PressureCurveShape.linear]!,
|
||||
isTrue);
|
||||
expect(
|
||||
mids[PressureCurveShape.linear]! >
|
||||
mids[PressureCurveShape.quadratic]!,
|
||||
isTrue);
|
||||
expect(
|
||||
mids[PressureCurveShape.quadratic]! >
|
||||
mids[PressureCurveShape.cubic]!,
|
||||
isTrue);
|
||||
expect(
|
||||
mids[PressureCurveShape.logarithmic]! >
|
||||
mids[PressureCurveShape.linear]!,
|
||||
isTrue);
|
||||
// Distinct midpoints (no two shapes collide at p=0.5).
|
||||
expect(mids.values.toSet().length, PressureCurveShape.values.length);
|
||||
});
|
||||
|
||||
test('shaped presets set expected gamma (except log)', () {
|
||||
expect(PressureCurve.shaped(PressureCurveShape.linear).gamma, 1.0);
|
||||
expect(PressureCurve.shaped(PressureCurveShape.soft).gamma, 0.6);
|
||||
expect(PressureCurve.shaped(PressureCurveShape.quadratic).gamma, 2.0);
|
||||
expect(PressureCurve.shaped(PressureCurveShape.cubic).gamma, 3.0);
|
||||
expect(PressureCurve.shaped(PressureCurveShape.sqrt).gamma, 0.5);
|
||||
expect(PressureCurve.shaped(PressureCurveShape.logarithmic).shape,
|
||||
PressureCurveShape.logarithmic);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user