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:
@@ -135,16 +135,15 @@ class BrushProfile {
|
||||
/// highlighter ≈ 0.02 so the existing pen/highlighter visuals are PRESERVED as
|
||||
/// the fountainPen/highlighter presets (no regression).
|
||||
const Map<BrushKind, BrushProfile> kBrushPresets = {
|
||||
// Fountain pen — spec §4: size~6, thinning 0.9, smoothing 0.55,
|
||||
// streamline 0.45, simulatePressure false, taper on, pressure pre-warped to
|
||||
// p² (Pow2 / quadratic = pressureGamma 2.0). Solid ink (opacity 1.0).
|
||||
// Fountain pen — Surface feel: lower streamline (less lag), higher thinning
|
||||
// for expressive width, pressure pre-warped to p² (Pow2 / quadratic).
|
||||
// Solid ink (opacity 1.0). See also tipVelocityWidthScale (ink starvation).
|
||||
BrushKind.fountainPen: BrushProfile(
|
||||
kind: BrushKind.fountainPen,
|
||||
baseWidthFraction: 0.006,
|
||||
pressureGamma: 2.0,
|
||||
// Was 0.9 — too aggressive on short CJK strokes (width collapses mid-glyph).
|
||||
pfThinning: 0.65,
|
||||
pfStreamline: 0.4,
|
||||
pfThinning: 0.75,
|
||||
pfStreamline: 0.22,
|
||||
pfSmoothing: 0.5,
|
||||
simulatePressure: false,
|
||||
capStart: true,
|
||||
@@ -154,15 +153,14 @@ const Map<BrushKind, BrushProfile> kBrushPresets = {
|
||||
opacity: 1.0,
|
||||
blendMultiply: false,
|
||||
),
|
||||
// Ballpoint — Krita-inspired "ink pen": near-constant width, SOLID opacity.
|
||||
// Pressure modulates WIDTH slightly (thinning 0.15), NOT alpha — translucent
|
||||
// srcOver stacking looked like accidental multiply when strokes overlapped.
|
||||
// Ballpoint — near-constant width, SOLID opacity. Lower streamline (~0.35)
|
||||
// for lower latency; thinning 0.12 keeps width almost flat.
|
||||
BrushKind.ballpoint: BrushProfile(
|
||||
kind: BrushKind.ballpoint,
|
||||
baseWidthFraction: 0.0022,
|
||||
pressureGamma: 1.0,
|
||||
pfThinning: 0.15,
|
||||
pfStreamline: 0.55,
|
||||
pfThinning: 0.12,
|
||||
pfStreamline: 0.35,
|
||||
pfSmoothing: 0.5,
|
||||
simulatePressure: false,
|
||||
capStart: true,
|
||||
@@ -171,18 +169,14 @@ const Map<BrushKind, BrushProfile> kBrushPresets = {
|
||||
opacity: 1.0,
|
||||
blendMultiply: false,
|
||||
),
|
||||
// Highlighter — spec §4: size~22, thinning 0.0 (constant width),
|
||||
// smoothing 0.4, streamline 0.5, square (uncapped) ends, translucent +
|
||||
// multiply build-up. opacity 0.35 / blendMultiply true are APPLIED via
|
||||
// resolveStrokePaint: the 0.35 is multiplied INTO the color's existing alpha
|
||||
// (the capture path ships a 0x80 / 50% translucent color), and the stroke
|
||||
// composites with BlendMode.multiply (cross-stroke overlap darkens = marker).
|
||||
// Highlighter — flat width (thinning 0), square (uncapped) ends, translucent +
|
||||
// multiply build-up. streamline 0.3 for a bit less lag on broad strokes.
|
||||
BrushKind.highlighter: BrushProfile(
|
||||
kind: BrushKind.highlighter,
|
||||
baseWidthFraction: 0.02,
|
||||
pressureGamma: 1.0,
|
||||
pfThinning: 0.0,
|
||||
pfStreamline: 0.5,
|
||||
pfStreamline: 0.3,
|
||||
pfSmoothing: 0.4,
|
||||
simulatePressure: false,
|
||||
capStart: false,
|
||||
@@ -191,15 +185,13 @@ const Map<BrushKind, BrushProfile> kBrushPresets = {
|
||||
opacity: 0.35,
|
||||
blendMultiply: true,
|
||||
),
|
||||
// Pencil — Krita-inspired: soft graphite, moderate translucency via √p, but
|
||||
// NEVER multiply blend (only highlighter uses multiply). Cap ~0.88 so overlaps
|
||||
// darken gently under srcOver without turning into marker blobs.
|
||||
// Pencil — soft graphite via √p, moderate translucency. streamline 0.25.
|
||||
BrushKind.pencil: BrushProfile(
|
||||
kind: BrushKind.pencil,
|
||||
baseWidthFraction: 0.003,
|
||||
pressureGamma: 0.5,
|
||||
pfThinning: 0.45,
|
||||
pfStreamline: 0.35,
|
||||
pfStreamline: 0.25,
|
||||
pfSmoothing: 0.45,
|
||||
simulatePressure: false,
|
||||
capStart: true,
|
||||
|
||||
33
lib/editor/engine/pen_physics.dart
Normal file
33
lib/editor/engine/pen_physics.dart
Normal file
@@ -0,0 +1,33 @@
|
||||
// lib/editor/engine/pen_physics.dart
|
||||
//
|
||||
// Simple physical tip model: modulate stroke width by tip velocity so fountain
|
||||
// ink feels slightly thinner when moving fast (starvation), while ballpoint
|
||||
// stays nearly velocity-invariant.
|
||||
//
|
||||
// TODO(pen-physics-wire): wired at capture in PenCanvas._toNormalized via
|
||||
// tip velocity × pressure. PDF editor path still uses brush gamma only.
|
||||
|
||||
import 'brush.dart';
|
||||
|
||||
/// Modulate width fraction by tip velocity (page-normalized units per second).
|
||||
///
|
||||
/// Fountain: faster → slightly thinner (ink starvation feel).
|
||||
/// Ballpoint: nearly ignore velocity.
|
||||
/// Pencil: mild thinning at speed.
|
||||
/// Highlighter: ignore velocity (flat marker).
|
||||
double tipVelocityWidthScale(BrushKind kind, double speedNormPerSec) {
|
||||
final speed =
|
||||
speedNormPerSec.isNaN || speedNormPerSec < 0 ? 0.0 : speedNormPerSec;
|
||||
// Reference: ~2 page-widths/sec ≈ fast handwriting; clamp influence to [0,1].
|
||||
final t = (speed / 2.0).clamp(0.0, 1.0);
|
||||
switch (kind) {
|
||||
case BrushKind.fountainPen:
|
||||
return 1.0 - 0.15 * t;
|
||||
case BrushKind.ballpoint:
|
||||
return 1.0 - 0.02 * t;
|
||||
case BrushKind.pencil:
|
||||
return 1.0 - 0.08 * t;
|
||||
case BrushKind.highlighter:
|
||||
return 1.0;
|
||||
}
|
||||
}
|
||||
@@ -11,7 +11,7 @@ class PredictedPoint {
|
||||
}
|
||||
|
||||
class StrokePredictor {
|
||||
StrokePredictor({this.lookaheadMs = 12});
|
||||
StrokePredictor({this.lookaheadMs = 8});
|
||||
|
||||
/// How far ahead to project, in milliseconds of recent velocity.
|
||||
final double lookaheadMs;
|
||||
|
||||
Reference in New Issue
Block a user