2022-10-29 15:39:25 +00:00
|
|
|
import 'dart:math';
|
|
|
|
import 'dart:ui';
|
2022-10-30 20:57:13 +00:00
|
|
|
|
2022-10-29 19:39:08 +00:00
|
|
|
import 'package:flutter/gestures.dart';
|
|
|
|
import 'package:flutter/material.dart';
|
2022-10-29 15:39:25 +00:00
|
|
|
|
2022-10-29 19:39:08 +00:00
|
|
|
import '../savesystem/line_loader.dart';
|
2022-10-29 18:06:20 +00:00
|
|
|
import '../savesystem/note_file.dart';
|
2022-10-29 15:39:25 +00:00
|
|
|
import 'document_types.dart';
|
|
|
|
import 'my_painter.dart';
|
|
|
|
|
|
|
|
enum Pen { eraser, pen, highlighter, selector }
|
|
|
|
|
|
|
|
class PaintController extends ChangeNotifier {
|
|
|
|
Pen activePen = Pen.pen;
|
|
|
|
List<Stroke> strokes = [];
|
|
|
|
final bool _allowDrawWithFinger = false;
|
|
|
|
|
2022-10-29 18:06:20 +00:00
|
|
|
PaintController(this.file);
|
|
|
|
|
|
|
|
final NoteFile file;
|
|
|
|
|
2022-10-29 15:39:25 +00:00
|
|
|
void changePen(Pen pen) {
|
|
|
|
activePen = pen;
|
|
|
|
notifyListeners();
|
|
|
|
}
|
|
|
|
|
|
|
|
double _calcTiltedWidth(double baseWidth, double tilt) {
|
|
|
|
if (tilt == .0) return baseWidth;
|
|
|
|
return baseWidth * tilt;
|
|
|
|
}
|
|
|
|
|
|
|
|
double _calcAngleDependentWidth(Point pt1, Point pt2, double basetickness) {
|
|
|
|
double dx = pt2.point.dx - pt1.point.dx;
|
|
|
|
double dy = pt2.point.dy - pt1.point.dy;
|
|
|
|
|
|
|
|
// todo those deltas to small to get an accurate direction!
|
|
|
|
|
|
|
|
double alpha = atan(dx / dy);
|
|
|
|
// alpha has range from 0 - 2pi
|
|
|
|
// we want 0.5 -1;
|
|
|
|
|
|
|
|
alpha /= (2 * pi * 2);
|
|
|
|
alpha += .5;
|
|
|
|
|
|
|
|
double thickness = basetickness * alpha;
|
|
|
|
return thickness;
|
|
|
|
}
|
|
|
|
|
2022-10-29 19:39:08 +00:00
|
|
|
void pointDownEvent(Offset offset, PointerDownEvent e) async {
|
|
|
|
if (_allowedToDraw(e)) {
|
2022-10-29 15:39:25 +00:00
|
|
|
// todo line drawn on edge where line left page
|
|
|
|
if (!a4Page.contains(offset)) return;
|
|
|
|
|
|
|
|
// todo handle other pens
|
2022-10-29 19:39:08 +00:00
|
|
|
if (activePen == Pen.eraser || activePen == Pen.selector) return;
|
2022-10-29 15:39:25 +00:00
|
|
|
|
2022-10-29 18:06:20 +00:00
|
|
|
int strokeid = strokes.isNotEmpty ? strokes.last.id + 1 : 0;
|
|
|
|
strokes.add(Stroke.fromPoints(
|
2022-10-29 19:39:08 +00:00
|
|
|
[Point(offset, _calcTiltedWidth(3.0, e.tilt))],
|
|
|
|
strokeid,
|
|
|
|
activePen == Pen.pen
|
|
|
|
? Colors.black26
|
|
|
|
: Colors.yellow.withOpacity(.5)));
|
2022-10-29 18:06:20 +00:00
|
|
|
file.addStroke(strokeid);
|
|
|
|
|
2022-10-29 15:39:25 +00:00
|
|
|
notifyListeners();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-10-29 19:39:08 +00:00
|
|
|
void pointUpEvent(PointerUpEvent e) {
|
2022-10-29 15:39:25 +00:00
|
|
|
if (activePen == Pen.eraser) return;
|
|
|
|
|
2022-10-29 19:39:08 +00:00
|
|
|
if (_allowedToDraw(e)) {
|
2022-10-29 18:06:20 +00:00
|
|
|
final lastStroke = strokes.last;
|
|
|
|
if (lastStroke.points.length <= 1) {
|
2022-10-29 15:39:25 +00:00
|
|
|
// if the line consists only of one point (point) add endpoint as the same to allow drawing a line
|
2022-10-29 18:06:20 +00:00
|
|
|
lastStroke.points.add(lastStroke.points.last);
|
|
|
|
file.addPoint(lastStroke.id, lastStroke.points.last);
|
2022-10-29 15:39:25 +00:00
|
|
|
notifyListeners();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-10-29 19:39:08 +00:00
|
|
|
/// check if pointer event is allowed to draw points
|
|
|
|
bool _allowedToDraw(PointerEvent event) {
|
|
|
|
return (_allowDrawWithFinger && event.kind == PointerDeviceKind.touch) ||
|
|
|
|
event.kind == PointerDeviceKind.stylus ||
|
|
|
|
(event.kind == PointerDeviceKind.mouse &&
|
|
|
|
event.buttons == kPrimaryMouseButton);
|
|
|
|
}
|
|
|
|
|
|
|
|
void pointMoveEvent(Offset offset, PointerMoveEvent event) {
|
2022-10-29 15:39:25 +00:00
|
|
|
if (!a4Page.contains(offset)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2022-10-29 19:39:08 +00:00
|
|
|
if (_allowedToDraw(event)) {
|
2022-10-29 15:39:25 +00:00
|
|
|
switch (activePen) {
|
|
|
|
case Pen.eraser:
|
|
|
|
// todo dynamic eraser size
|
|
|
|
final eraserrect = Rect.fromCircle(center: offset, radius: 3);
|
|
|
|
for (final stroke in strokes) {
|
|
|
|
// check if delete action was within bounding rect of stroke
|
|
|
|
if (stroke.getBoundingRect().contains(offset)) {
|
|
|
|
// check if eraser hit an point within its range
|
|
|
|
for (final pt in stroke.points) {
|
|
|
|
if (eraserrect.contains(pt.point)) {
|
2022-10-29 18:06:20 +00:00
|
|
|
file.removeStroke(stroke.id);
|
2022-10-29 15:39:25 +00:00
|
|
|
strokes.remove(stroke);
|
|
|
|
notifyListeners();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case Pen.pen:
|
2022-10-29 19:39:08 +00:00
|
|
|
case Pen.highlighter:
|
2022-10-29 15:39:25 +00:00
|
|
|
final pts = strokes.last.points;
|
|
|
|
if (pts.last.point == offset) return;
|
|
|
|
|
2022-10-29 19:39:08 +00:00
|
|
|
double newWidth = _calcTiltedWidth(5.0, event.tilt);
|
2022-10-29 15:39:25 +00:00
|
|
|
if (strokes.last.points.length > 1) {
|
|
|
|
newWidth = _calcAngleDependentWidth(
|
|
|
|
pts.last, pts[pts.length - 2], newWidth);
|
|
|
|
}
|
|
|
|
|
2022-10-29 18:06:20 +00:00
|
|
|
Point p = Point(offset, newWidth);
|
|
|
|
strokes.last.addPoint(p);
|
2022-10-31 18:29:58 +00:00
|
|
|
// todo do a batch commit per stroke
|
2022-10-29 18:06:20 +00:00
|
|
|
file.addPoint(strokes.last.id, p);
|
2022-10-29 15:39:25 +00:00
|
|
|
break;
|
|
|
|
case Pen.selector:
|
|
|
|
// TODO: Handle this case.
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
notifyListeners();
|
|
|
|
}
|
|
|
|
}
|
2022-10-29 18:06:20 +00:00
|
|
|
|
|
|
|
Future<void> loadStrokesFromFile() async {
|
|
|
|
strokes = await file.loadStrokes();
|
|
|
|
notifyListeners();
|
|
|
|
}
|
2022-10-29 15:39:25 +00:00
|
|
|
}
|