100 lines
2.9 KiB
Dart
100 lines
2.9 KiB
Dart
import 'dart:ui';
|
|
|
|
/// Represents a single stroke/line drawn on the canvas.
|
|
///
|
|
/// Points are stored in normalized coordinates (0.0 to 1.0) where:
|
|
/// - (0, 0) is the top-left corner of the drawing area
|
|
/// - (1, 1) is the bottom-right corner of the drawing area
|
|
///
|
|
/// This allows drawings to scale correctly when the canvas size changes.
|
|
class DrawingLine {
|
|
/// The minimal squared distance between to points which are normalized so that this point is allowed to be added to the line
|
|
static const minNormalizedPointDistanceSquared = 0.001 * 0.001;
|
|
|
|
/// Points in normalized coordinates (0.0 to 1.0)
|
|
final List<Offset> points;
|
|
|
|
/// Color of the line (stored as ARGB integer for JSON serialization)
|
|
final Color color;
|
|
|
|
/// Stroke width in normalized units (relative to canvas width)
|
|
/// A value of 0.01 means the stroke is 1% of the canvas width
|
|
final double strokeWidth;
|
|
|
|
const DrawingLine({
|
|
required this.points,
|
|
required this.color,
|
|
required this.strokeWidth,
|
|
});
|
|
|
|
/// Creates a DrawingLine from JSON data.
|
|
factory DrawingLine.fromJson(Map<String, dynamic> json) {
|
|
final pointsList = (json['points'] as List)
|
|
.map(
|
|
(p) => Offset((p['x'] as num).toDouble(), (p['y'] as num).toDouble()),
|
|
)
|
|
.toList();
|
|
|
|
return DrawingLine(
|
|
points: pointsList,
|
|
color: Color(json['color'] as int),
|
|
strokeWidth: (json['strokeWidth'] as num).toDouble(),
|
|
);
|
|
}
|
|
|
|
/// Converts this line to a JSON-serializable map.
|
|
Map<String, dynamic> toJson() {
|
|
return {
|
|
'points': points.map((p) => {'x': p.dx, 'y': p.dy}).toList(),
|
|
'color': color.toARGB32(),
|
|
'strokeWidth': strokeWidth,
|
|
};
|
|
}
|
|
|
|
/// Creates a copy of this line with an additional point.
|
|
DrawingLine addPoint(Offset normalizedPoint) {
|
|
return DrawingLine(
|
|
points: [...points, normalizedPoint],
|
|
color: color,
|
|
strokeWidth: strokeWidth,
|
|
);
|
|
}
|
|
|
|
bool isPointTooClose(Offset nextNormalizedPoint) {
|
|
if (points.isEmpty) {
|
|
return false;
|
|
}
|
|
return (points.last - nextNormalizedPoint).distanceSquared <
|
|
minNormalizedPointDistanceSquared;
|
|
}
|
|
|
|
/// Creates a copy with updated points.
|
|
DrawingLine copyWith({
|
|
List<Offset>? points,
|
|
Color? color,
|
|
double? strokeWidth,
|
|
}) {
|
|
return DrawingLine(
|
|
points: points ?? this.points,
|
|
color: color ?? this.color,
|
|
strokeWidth: strokeWidth ?? this.strokeWidth,
|
|
);
|
|
}
|
|
|
|
@override
|
|
bool operator ==(Object other) {
|
|
if (identical(this, other)) return true;
|
|
if (other is! DrawingLine) return false;
|
|
if (points.length != other.points.length) return false;
|
|
if (color != other.color) return false;
|
|
if (strokeWidth != other.strokeWidth) return false;
|
|
for (int i = 0; i < points.length; i++) {
|
|
if (points[i] != other.points[i]) return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
@override
|
|
int get hashCode => Object.hash(Object.hashAll(points), color, strokeWidth);
|
|
}
|