Skip to content

Commit

Permalink
feat: migrate to $1 unistroke recognizer
Browse files Browse the repository at this point in the history
  • Loading branch information
adil192 committed Nov 30, 2023
1 parent 71f98d4 commit 5e34bd8
Show file tree
Hide file tree
Showing 9 changed files with 83 additions and 124 deletions.
41 changes: 19 additions & 22 deletions lib/components/canvas/_canvas_painter.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import 'dart:math';
import 'dart:ui';

import 'package:flutter/material.dart' hide TextStyle;
import 'package:interactive_shape_recognition/interactive_shape_recognition.dart';
import 'package:one_dollar_unistroke_recognizer/one_dollar_unistroke_recognizer.dart';
import 'package:path_drawing/path_drawing.dart';
import 'package:saber/components/canvas/_circle_stroke.dart';
import 'package:saber/components/canvas/_rectangle_stroke.dart';
Expand Down Expand Up @@ -167,8 +167,8 @@ class CanvasPainter extends CustomPainter {
}

void _drawDetectedShape(Canvas canvas) {
if (ShapePen.detectedShape == null) return;
if (ShapePen.detectedShape!.shape == Shape.unknown) return;
final shape = ShapePen.detectedShape;
if (shape == null) return;

final color = currentStroke?.strokeProperties.color.withInversion(invert)
?? Colors.black;
Expand All @@ -177,27 +177,24 @@ class CanvasPainter extends CustomPainter {
..style = PaintingStyle.stroke
..strokeWidth = currentStroke?.strokeProperties.size ?? 3;

switch (ShapePen.detectedShape!.shape) {
case Shape.line:
canvas.drawLine(
ShapePen.detectedShape!.firstPoint,
ShapePen.detectedShape!.lastPoint,
switch (shape.name) {
case null:
break;
case DefaultUnistrokeNames.line:
final (firstPoint, lastPoint) = shape.convertToLine();
canvas.drawLine(firstPoint, lastPoint, shapePaint);
case DefaultUnistrokeNames.rectangle:
final rect = shape.convertToRect();
canvas.drawRect(rect, shapePaint);
case DefaultUnistrokeNames.circle:
final (center, radius) = shape.convertToCircle();
canvas.drawCircle(center, radius, shapePaint);
case DefaultUnistrokeNames.triangle:
final polygon = shape.convertToCanonicalPolygon();
canvas.drawPath(
Path()..addPolygon(polygon, true),
shapePaint,
);
case Shape.rectangle:
canvas.drawRect(
ShapePen.detectedShape!.generateRectangle(),
shapePaint,
);
case Shape.circle:
final circle = ShapePen.detectedShape!.generateCircle();
canvas.drawCircle(
circle.$2,
circle.$1,
shapePaint,
);
case Shape.unknown:
throw StateError('Shape.unknown should have an early return');
}
}

Expand Down
35 changes: 7 additions & 28 deletions lib/components/canvas/_circle_stroke.dart
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import 'dart:math';

import 'package:flutter/material.dart';
import 'package:interactive_shape_recognition/interactive_shape_recognition.dart';
import 'package:one_dollar_unistroke_recognizer/one_dollar_unistroke_recognizer.dart';
import 'package:saber/components/canvas/_stroke.dart';
import 'package:saber/data/tools/shape_pen.dart';
import 'package:saber/data/tools/stroke_properties.dart';
Expand Down Expand Up @@ -108,31 +108,12 @@ class CircleStroke extends Stroke {

@override
@Deprecated('We already know the shape is a circle.')
DetectedShape getDetectedShape() {
final rect = Rect.fromCircle(center: center, radius: radius);
return DetectedShape(
shape: Shape.circle,
maybeCircle: true,
maybeLine: false,
maybeRectangle: false,
enclosingRect: [
rect.topLeft,
rect.topRight,
rect.bottomRight,
rect.bottomLeft,
],
convexHull: [
rect.topLeft,
rect.topRight,
rect.bottomRight,
rect.bottomLeft,
],
firstPoint: rect.topCenter,
lastPoint: rect.bottomCenter,
rLenPch: 0,
rThinness: 0,
rAltAch: 0,
rPchPer: 0,
RecognizedUnistroke<DefaultUnistrokeNames> detectShape() {
return RecognizedUnistroke(
DefaultUnistrokeNames.circle,
1,
originalPoints: polygon,
referenceUnistrokes: default$1Unistrokes,
);
}

Expand All @@ -144,6 +125,4 @@ class CircleStroke extends Stroke {
center: center,
radius: radius,
);

// TODO(adil192): Add HighlighterStroke class and move canvas drawing logic to Stroke.
}
32 changes: 7 additions & 25 deletions lib/components/canvas/_rectangle_stroke.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import 'package:flutter/material.dart';
import 'package:interactive_shape_recognition/interactive_shape_recognition.dart';
import 'package:one_dollar_unistroke_recognizer/one_dollar_unistroke_recognizer.dart';
import 'package:saber/components/canvas/_stroke.dart';
import 'package:saber/data/tools/shape_pen.dart';
import 'package:saber/data/tools/stroke_properties.dart';
Expand Down Expand Up @@ -124,30 +124,12 @@ class RectangleStroke extends Stroke {

@override
@Deprecated('We already know the shape is a rectangle.')
DetectedShape getDetectedShape() {
return DetectedShape(
shape: Shape.rectangle,
maybeCircle: false,
maybeLine: false,
maybeRectangle: true,
enclosingRect: [
rect.topLeft,
rect.topRight,
rect.bottomRight,
rect.bottomLeft,
],
convexHull: [
rect.topLeft,
rect.topRight,
rect.bottomRight,
rect.bottomLeft,
],
firstPoint: rect.topCenter,
lastPoint: rect.bottomCenter,
rLenPch: 0,
rThinness: 0,
rAltAch: 0,
rPchPer: 0,
RecognizedUnistroke<DefaultUnistrokeNames> detectShape() {
return RecognizedUnistroke(
DefaultUnistrokeNames.rectangle,
1,
originalPoints: polygon,
referenceUnistrokes: default$1Unistrokes,
);
}

Expand Down
12 changes: 9 additions & 3 deletions lib/components/canvas/_stroke.dart
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import 'dart:math' hide atan2;

import 'package:flutter/material.dart';
import 'package:interactive_shape_recognition/interactive_shape_recognition.dart';
import 'package:logging/logging.dart';
import 'package:one_dollar_unistroke_recognizer/one_dollar_unistroke_recognizer.dart';
import 'package:perfect_freehand/perfect_freehand.dart';
import 'package:saber/components/canvas/_circle_stroke.dart';
import 'package:saber/components/canvas/_rectangle_stroke.dart';
Expand Down Expand Up @@ -126,6 +126,12 @@ class Stroke {
_polygonNeedsUpdating = true;
}

void addPoints(List<Offset> points) {
for (final point in points) {
addPoint(point);
}
}

void popFirstPoint() {
points.removeAt(0);
_polygonNeedsUpdating = true;
Expand Down Expand Up @@ -237,9 +243,9 @@ class Stroke {
}
}

DetectedShape? getDetectedShape() {
RecognizedUnistroke<DefaultUnistrokeNames>? detectShape() {
if (points.length < 3) return null;
return detectShape(
return recognizeUnistroke(
points.map((point) => Offset(point.x, point.y)).toList(),
);
}
Expand Down
4 changes: 2 additions & 2 deletions lib/components/canvas/canvas.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import 'package:flutter/material.dart';
import 'package:interactive_shape_recognition/interactive_shape_recognition.dart';
import 'package:one_dollar_unistroke_recognizer/one_dollar_unistroke_recognizer.dart';
import 'package:onyxsdk_pen/onyxsdk_pen_area.dart';
import 'package:saber/components/canvas/_stroke.dart';
import 'package:saber/components/canvas/image/editor_image.dart';
Expand Down Expand Up @@ -31,7 +31,7 @@ class Canvas extends StatelessWidget {
final bool textEditing;
final EditorCoreInfo coreInfo;
final Stroke? currentStroke;
final DetectedShape? currentStrokeDetectedShape;
final RecognizedUnistroke<DefaultUnistrokeNames>? currentStrokeDetectedShape;
final SelectResult? currentSelection;

final void Function(EditorImage image)? setAsBackground;
Expand Down
4 changes: 2 additions & 2 deletions lib/components/canvas/inner_canvas.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import 'package:defer_pointer/defer_pointer.dart';
import 'package:flutter/material.dart';
import 'package:flutter_quill/flutter_quill.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:interactive_shape_recognition/interactive_shape_recognition.dart';
import 'package:one_dollar_unistroke_recognizer/one_dollar_unistroke_recognizer.dart';
import 'package:saber/components/canvas/_canvas_background_painter.dart';
import 'package:saber/components/canvas/_canvas_painter.dart';
import 'package:saber/components/canvas/_stroke.dart';
Expand Down Expand Up @@ -44,7 +44,7 @@ class InnerCanvas extends StatefulWidget {
final bool textEditing;
final EditorCoreInfo coreInfo;
final Stroke? currentStroke;
final DetectedShape? currentStrokeDetectedShape;
final RecognizedUnistroke<DefaultUnistrokeNames>? currentStrokeDetectedShape;
final SelectResult? currentSelection;
final void Function(EditorImage image)? setAsBackground;
final ValueChanged<RenderObject>? onRenderObjectChange;
Expand Down
45 changes: 28 additions & 17 deletions lib/data/tools/shape_pen.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import 'dart:async';

import 'package:flutter/material.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:interactive_shape_recognition/interactive_shape_recognition.dart';
import 'package:logging/logging.dart';
import 'package:one_dollar_unistroke_recognizer/one_dollar_unistroke_recognizer.dart';
import 'package:saber/components/canvas/_circle_stroke.dart';
import 'package:saber/components/canvas/_rectangle_stroke.dart';
import 'package:saber/components/canvas/_stroke.dart';
Expand All @@ -28,9 +28,9 @@ class ShapePen extends Pen {

static const IconData shapePenIcon = FontAwesomeIcons.shapes;

static DetectedShape? detectedShape;
static RecognizedUnistroke<DefaultUnistrokeNames>? detectedShape;
void _detectShape() {
detectedShape = Pen.currentStroke?.getDetectedShape();
detectedShape = Pen.currentStroke?.detectShape();
}

static Timer? _detectShapeDebouncer;
Expand All @@ -57,40 +57,51 @@ class ShapePen extends Pen {

if (detectedShape == null) return rawStroke;

switch (detectedShape.shape) {
case Shape.unknown:
switch (detectedShape.name) {
case null:
log.info('Detected unknown shape');
return rawStroke;
case Shape.line:
log.info('Detected line: ${detectedShape.firstPoint} -> ${detectedShape.lastPoint}');
case DefaultUnistrokeNames.line:
final (firstPoint, lastPoint) = detectedShape.convertToLine();
log.info('Detected line: $firstPoint -> $lastPoint');
return Stroke(
strokeProperties: rawStroke.strokeProperties,
pageIndex: rawStroke.pageIndex,
penType: rawStroke.penType,
)
..addPoint(detectedShape.firstPoint)
..addPoint(detectedShape.lastPoint)
..addPoint(detectedShape.lastPoint)
..addPoint(firstPoint)
..addPoint(lastPoint)
..addPoint(lastPoint)
..isComplete = true;
case Shape.rectangle:
final rect = detectedShape.generateRectangle();
case DefaultUnistrokeNames.rectangle:
final rect = detectedShape.convertToRect();
log.info('Detected rectangle: $rect');
return RectangleStroke(
strokeProperties: rawStroke.strokeProperties,
pageIndex: rawStroke.pageIndex,
penType: rawStroke.penType,
rect: rect,
);
case Shape.circle:
final circle = detectedShape.generateCircle();
log.info('Detected circle: $circle');
case DefaultUnistrokeNames.circle:
final (center, radius) = detectedShape.convertToCircle();
log.info('Detected circle: c=$center, r=$radius');
return CircleStroke(
strokeProperties: rawStroke.strokeProperties,
pageIndex: rawStroke.pageIndex,
penType: rawStroke.penType,
radius: circle.$1,
center: circle.$2,
radius: radius,
center: center,
);
case DefaultUnistrokeNames.triangle:
final polygon = detectedShape.convertToCanonicalPolygon();
log.info('Detected triangle');
return Stroke(
strokeProperties: rawStroke.strokeProperties,
pageIndex: rawStroke.pageIndex,
penType: rawStroke.penType,
)
..addPoints(polygon)
..isComplete = true;
}
}
}
32 changes: 8 additions & 24 deletions pubspec.lock
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,6 @@ packages:
url: "https://pub.dev"
source: hosted
version: "3.4.9"
area_polygon:
dependency: transitive
description:
name: area_polygon
sha256: ed3072f73a922b5e7cfca79f274a750123108f0ccf3510579d2eac88424ba13a
url: "https://pub.dev"
source: hosted
version: "1.0.2"
args:
dependency: transitive
description:
Expand Down Expand Up @@ -162,14 +154,6 @@ packages:
url: "https://pub.dev"
source: hosted
version: "3.1.1"
convex_hull:
dependency: transitive
description:
name: convex_hull
sha256: "67489a600f0ae7b4a4851e896bbea01cf9f599af520ab8ab8390332d59da340d"
url: "https://pub.dev"
source: hosted
version: "1.1.2"
cookie_jar:
dependency: transitive
description:
Expand Down Expand Up @@ -622,14 +606,6 @@ packages:
description: flutter
source: sdk
version: "0.0.0"
interactive_shape_recognition:
dependency: "direct main"
description:
name: interactive_shape_recognition
sha256: "9d44b70c45d60c81bc2a3340d25ee74e54bb616711d0592c274162bdc4e45742"
url: "https://pub.dev"
source: hosted
version: "1.2.0"
intl:
dependency: "direct main"
description:
Expand Down Expand Up @@ -760,6 +736,14 @@ packages:
url: "https://github.com/nextcloud/neon"
source: git
version: "1.0.0"
one_dollar_unistroke_recognizer:
dependency: "direct main"
description:
name: one_dollar_unistroke_recognizer
sha256: "478ae7ccbe544d6a696c6fed222bd71f64f697f837fbbee158d5d0f6956e7f48"
url: "https://pub.dev"
source: hosted
version: "0.6.1"
onyxsdk_pen:
dependency: "direct main"
description:
Expand Down
2 changes: 1 addition & 1 deletion pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ dependencies:

yaru: ^1.1.0

interactive_shape_recognition: ^1.1.1
one_dollar_unistroke_recognizer: ^0.6.1

meta: ^1.0.0

Expand Down

0 comments on commit 5e34bd8

Please sign in to comment.