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:
@@ -21,6 +21,7 @@ import '../../models/ink_stroke.dart';
|
||||
import '../../models/pen_tool.dart';
|
||||
import '../../models/pointer_device_kind.dart';
|
||||
import '../canvas/pen_stroke.dart';
|
||||
import 'brush.dart';
|
||||
|
||||
part 'stroke_model.freezed.dart';
|
||||
part 'stroke_model.g.dart';
|
||||
@@ -74,6 +75,15 @@ abstract class EditorStroke with _$EditorStroke {
|
||||
@Default(false) bool filled,
|
||||
String? textContent,
|
||||
@Default(14.0) double fontSize,
|
||||
// Brush the stroke was drawn with. NOT serialized (increment 1 keeps brush
|
||||
// out of persistence / InkStroke round-trip — see TODO(brush-persist)); it
|
||||
// is an IN-MEMORY render hint only, so the committed render path can resolve
|
||||
// each stroke's perfect_freehand geometry. Defaults to fountainPen so loaded
|
||||
// (deserialized) strokes keep the legacy pen visual.
|
||||
// ignore: invalid_annotation_target
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@Default(BrushKind.fountainPen)
|
||||
BrushKind brush,
|
||||
}) = _EditorStroke;
|
||||
|
||||
/// Convenience constructor that generates a uuid [id] when none is supplied.
|
||||
@@ -86,6 +96,7 @@ abstract class EditorStroke with _$EditorStroke {
|
||||
bool filled = false,
|
||||
String? textContent,
|
||||
double fontSize = 14.0,
|
||||
BrushKind brush = BrushKind.fountainPen,
|
||||
}) =>
|
||||
EditorStroke(
|
||||
id: id ?? _uuid.v4(),
|
||||
@@ -96,6 +107,7 @@ abstract class EditorStroke with _$EditorStroke {
|
||||
filled: filled,
|
||||
textContent: textContent,
|
||||
fontSize: fontSize,
|
||||
brush: brush,
|
||||
);
|
||||
|
||||
factory EditorStroke.fromJson(Map<String, dynamic> json) =>
|
||||
@@ -118,6 +130,7 @@ abstract class EditorStroke with _$EditorStroke {
|
||||
},
|
||||
color: stroke.color,
|
||||
width: stroke.width,
|
||||
brush: stroke.brush,
|
||||
);
|
||||
|
||||
/// Lossless adapter from the freezed/JSON [InkStroke] model.
|
||||
|
||||
Reference in New Issue
Block a user