Files
sheetless/lib/features/sheet_viewer/drawing/drawing_canvas.dart
2026-02-05 18:23:41 +01:00

116 lines
3.2 KiB
Dart

import 'package:flutter/material.dart';
import 'drawing_controller.dart';
import 'drawing_line.dart';
/// Custom painter that renders drawing lines on a canvas.
///
/// Converts normalized coordinates (0-1) to actual canvas coordinates
/// based on the provided canvas size.
class DrawingPainter extends CustomPainter {
final List<DrawingLine> lines;
final DrawingLine? currentLine;
final Size canvasSize;
DrawingPainter({
required this.lines,
required this.currentLine,
required this.canvasSize,
});
@override
void paint(Canvas canvas, Size size) {
// Draw all completed lines
for (final line in lines) {
_drawLine(canvas, line);
}
// Draw the current line being drawn
if (currentLine != null) {
_drawLine(canvas, currentLine!);
}
}
void _drawLine(Canvas canvas, DrawingLine line) {
if (line.points.length < 2) return;
final paint = Paint()
..color = line.color
..strokeWidth = line.strokeWidth * canvasSize.width
..strokeCap = StrokeCap.round
..strokeJoin = StrokeJoin.round
..style = PaintingStyle.stroke
..isAntiAlias = true;
// Create path from normalized points
final path = Path();
final firstPoint = _toCanvasPoint(line.points.first);
path.moveTo(firstPoint.dx, firstPoint.dy);
// Use quadratic bezier curves for smooth lines
if (line.points.length == 2) {
final endPoint = _toCanvasPoint(line.points.last);
path.lineTo(endPoint.dx, endPoint.dy);
} else {
for (int i = 1; i < line.points.length - 1; i++) {
final p0 = _toCanvasPoint(line.points[i]);
final p1 = _toCanvasPoint(line.points[i + 1]);
final midPoint = Offset((p0.dx + p1.dx) / 2, (p0.dy + p1.dy) / 2);
path.quadraticBezierTo(p0.dx, p0.dy, midPoint.dx, midPoint.dy);
}
// Draw to the last point
final lastPoint = _toCanvasPoint(line.points.last);
path.lineTo(lastPoint.dx, lastPoint.dy);
}
canvas.drawPath(path, paint);
}
/// Converts a normalized point (0-1) to canvas coordinates.
Offset _toCanvasPoint(Offset normalizedPoint) {
return Offset(
normalizedPoint.dx * canvasSize.width,
normalizedPoint.dy * canvasSize.height,
);
}
@override
bool shouldRepaint(covariant DrawingPainter oldDelegate) {
return lines != oldDelegate.lines ||
currentLine != oldDelegate.currentLine ||
canvasSize != oldDelegate.canvasSize;
}
}
/// A widget that displays drawing lines on a transparent canvas.
///
/// This widget only shows the drawings, it doesn't handle input.
/// Use [DrawingCanvas] or [DrawingBoard] for input handling.
class DrawingOverlay extends StatelessWidget {
final DrawingController controller;
final Size canvasSize;
const DrawingOverlay({
super.key,
required this.controller,
required this.canvasSize,
});
@override
Widget build(BuildContext context) {
return ListenableBuilder(
listenable: controller,
builder: (context, _) {
return CustomPaint(
size: canvasSize,
painter: DrawingPainter(
lines: controller.lines,
currentLine: controller.currentLine,
canvasSize: canvasSize,
),
);
},
);
}
}