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 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 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 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? 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); }