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:
@@ -302,7 +302,14 @@ mixin _$EditorStroke {
|
||||
double get width => throw _privateConstructorUsedError;
|
||||
bool get filled => throw _privateConstructorUsedError;
|
||||
String? get textContent => throw _privateConstructorUsedError;
|
||||
double get fontSize => throw _privateConstructorUsedError;
|
||||
double get fontSize =>
|
||||
throw _privateConstructorUsedError; // 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.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
BrushKind get brush => throw _privateConstructorUsedError;
|
||||
|
||||
/// Serializes this EditorStroke to a JSON map.
|
||||
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
|
||||
@@ -330,6 +337,7 @@ abstract class $EditorStrokeCopyWith<$Res> {
|
||||
bool filled,
|
||||
String? textContent,
|
||||
double fontSize,
|
||||
@JsonKey(includeFromJson: false, includeToJson: false) BrushKind brush,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -356,6 +364,7 @@ class _$EditorStrokeCopyWithImpl<$Res, $Val extends EditorStroke>
|
||||
Object? filled = null,
|
||||
Object? textContent = freezed,
|
||||
Object? fontSize = null,
|
||||
Object? brush = null,
|
||||
}) {
|
||||
return _then(
|
||||
_value.copyWith(
|
||||
@@ -391,6 +400,10 @@ class _$EditorStrokeCopyWithImpl<$Res, $Val extends EditorStroke>
|
||||
? _value.fontSize
|
||||
: fontSize // ignore: cast_nullable_to_non_nullable
|
||||
as double,
|
||||
brush: null == brush
|
||||
? _value.brush
|
||||
: brush // ignore: cast_nullable_to_non_nullable
|
||||
as BrushKind,
|
||||
)
|
||||
as $Val,
|
||||
);
|
||||
@@ -415,6 +428,7 @@ abstract class _$$EditorStrokeImplCopyWith<$Res>
|
||||
bool filled,
|
||||
String? textContent,
|
||||
double fontSize,
|
||||
@JsonKey(includeFromJson: false, includeToJson: false) BrushKind brush,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -440,6 +454,7 @@ class __$$EditorStrokeImplCopyWithImpl<$Res>
|
||||
Object? filled = null,
|
||||
Object? textContent = freezed,
|
||||
Object? fontSize = null,
|
||||
Object? brush = null,
|
||||
}) {
|
||||
return _then(
|
||||
_$EditorStrokeImpl(
|
||||
@@ -475,6 +490,10 @@ class __$$EditorStrokeImplCopyWithImpl<$Res>
|
||||
? _value.fontSize
|
||||
: fontSize // ignore: cast_nullable_to_non_nullable
|
||||
as double,
|
||||
brush: null == brush
|
||||
? _value.brush
|
||||
: brush // ignore: cast_nullable_to_non_nullable
|
||||
as BrushKind,
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -492,6 +511,8 @@ class _$EditorStrokeImpl extends _EditorStroke {
|
||||
this.filled = false,
|
||||
this.textContent,
|
||||
this.fontSize = 14.0,
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
this.brush = BrushKind.fountainPen,
|
||||
}) : _points = points,
|
||||
super._();
|
||||
|
||||
@@ -525,10 +546,18 @@ class _$EditorStrokeImpl extends _EditorStroke {
|
||||
@override
|
||||
@JsonKey()
|
||||
final 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.
|
||||
@override
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
final BrushKind brush;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'EditorStroke(id: $id, points: $points, tool: $tool, color: $color, width: $width, filled: $filled, textContent: $textContent, fontSize: $fontSize)';
|
||||
return 'EditorStroke(id: $id, points: $points, tool: $tool, color: $color, width: $width, filled: $filled, textContent: $textContent, fontSize: $fontSize, brush: $brush)';
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -545,7 +574,8 @@ class _$EditorStrokeImpl extends _EditorStroke {
|
||||
(identical(other.textContent, textContent) ||
|
||||
other.textContent == textContent) &&
|
||||
(identical(other.fontSize, fontSize) ||
|
||||
other.fontSize == fontSize));
|
||||
other.fontSize == fontSize) &&
|
||||
(identical(other.brush, brush) || other.brush == brush));
|
||||
}
|
||||
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@@ -560,6 +590,7 @@ class _$EditorStrokeImpl extends _EditorStroke {
|
||||
filled,
|
||||
textContent,
|
||||
fontSize,
|
||||
brush,
|
||||
);
|
||||
|
||||
/// Create a copy of EditorStroke
|
||||
@@ -586,6 +617,8 @@ abstract class _EditorStroke extends EditorStroke {
|
||||
final bool filled,
|
||||
final String? textContent,
|
||||
final double fontSize,
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
final BrushKind brush,
|
||||
}) = _$EditorStrokeImpl;
|
||||
_EditorStroke._() : super._();
|
||||
|
||||
@@ -607,7 +640,14 @@ abstract class _EditorStroke extends EditorStroke {
|
||||
@override
|
||||
String? get textContent;
|
||||
@override
|
||||
double get fontSize;
|
||||
double get 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.
|
||||
@override
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
BrushKind get brush;
|
||||
|
||||
/// Create a copy of EditorStroke
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
|
||||
Reference in New Issue
Block a user