feat(pen): extensible brush model (4 brushes)
All checks were successful
CI / Windows build (push) Successful in 14m54s
All checks were successful
CI / Windows build (push) Successful in 14m54s
Replace the 2-tool ink system with a data-driven, Krita-style BrushProfile (lib/editor/engine/brush.dart). Adding a brush is a const map entry, not render-path branching. Four presets from the rnote/krita spec: - fountain pen: quadratic (p^2) pressure, wide dynamic width - ballpoint: near-constant width (thinning 0.15) - highlighter: flat width, square caps - pencil: sqrt(p) pressure, moderate width Pressure is pre-warped per brush via PressureCurve(gamma) before perfect_freehand; geometry fields (thinning/streamline/smoothing/ caps) flow through the shared stroke recipe so the PDF overlay and the note/slide PenCanvas both honor the brush. Brush kind is now persisted on the stroke model. Picker added to all three toolbars. Opacity/multiply and pencil grain are carried as data but not yet composited (TODO brush-opacity / brush-texture); this increment is width + pressure-curve differentiation. analyze clean, 283 tests.
This commit is contained in:
@@ -8,8 +8,9 @@ import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:perfect_freehand/perfect_freehand.dart' as pf;
|
||||
|
||||
import '../engine/brush.dart';
|
||||
import '../engine/stroke_geometry.dart'
|
||||
show kDefaultPenThinning, kPenSmoothing, kPenStreamline;
|
||||
show freehandOutlinePoints, kDefaultPenThinning;
|
||||
import 'pen_stroke.dart';
|
||||
|
||||
/// Builds a filled outline [Path] for one stroke (already scaled to pixels).
|
||||
@@ -39,22 +40,19 @@ Path buildStrokePath(
|
||||
)
|
||||
.toList();
|
||||
|
||||
final outline = pf.getStroke(
|
||||
pfPoints,
|
||||
options: pf.StrokeOptions(
|
||||
size: pixelWidth,
|
||||
// Highlighter keeps a constant width (no thinning); pen uses the
|
||||
// configurable [thinning] so real Surface-Pen pressure changes width.
|
||||
thinning: isHighlighter ? 0.0 : thinning,
|
||||
smoothing: kPenSmoothing,
|
||||
streamline: kPenStreamline,
|
||||
// Real stylus pressure → don't simulate; no pressure → let freehand fake
|
||||
// it based on velocity. (perfect_freehand 2.x honors REAL pressure when
|
||||
// simulatePressure is false — 1.0.4 ignored it, which made width
|
||||
// unresponsive to pen force.)
|
||||
simulatePressure: !hasRealPressure && !isHighlighter,
|
||||
isComplete: isComplete,
|
||||
),
|
||||
// Route through THE shared recipe (stroke_geometry.freehandOutlinePoints) so
|
||||
// this PDF-overlay path and the note/slide path can never diverge (R7), and
|
||||
// resolve the stroke's brush so each brush renders with its own
|
||||
// thinning/streamline/smoothing/caps (spec §4). Pressure was already
|
||||
// pre-warped by the brush's gamma at capture, so it is baked into pfPoints.
|
||||
final outline = freehandOutlinePoints(
|
||||
pfPoints: pfPoints,
|
||||
size: pixelWidth,
|
||||
isHighlighter: isHighlighter,
|
||||
hasRealPressure: hasRealPressure,
|
||||
isComplete: isComplete,
|
||||
thinning: thinning,
|
||||
brush: brushProfileFor(stroke.brush),
|
||||
);
|
||||
|
||||
final path = Path();
|
||||
|
||||
Reference in New Issue
Block a user