Skip to content

Commit

Permalink
feat: snap lines to horizontal/vertical
Browse files Browse the repository at this point in the history
  • Loading branch information
adil192 committed Nov 30, 2023
1 parent 5e34bd8 commit 5806b19
Show file tree
Hide file tree
Showing 3 changed files with 76 additions and 2 deletions.
3 changes: 2 additions & 1 deletion lib/components/canvas/_canvas_painter.dart
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,8 @@ class CanvasPainter extends CustomPainter {
case null:
break;
case DefaultUnistrokeNames.line:
final (firstPoint, lastPoint) = shape.convertToLine();
var (firstPoint, lastPoint) = shape.convertToLine();
(firstPoint, lastPoint) = ShapePen.snapLine(firstPoint, lastPoint);
canvas.drawLine(firstPoint, lastPoint, shapePaint);
case DefaultUnistrokeNames.rectangle:
final rect = shape.convertToRect();
Expand Down
26 changes: 25 additions & 1 deletion lib/data/tools/shape_pen.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import 'dart:async';
import 'dart:math' as math;

import 'package:flutter/material.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
Expand Down Expand Up @@ -62,7 +63,8 @@ class ShapePen extends Pen {
log.info('Detected unknown shape');
return rawStroke;
case DefaultUnistrokeNames.line:
final (firstPoint, lastPoint) = detectedShape.convertToLine();
var (firstPoint, lastPoint) = detectedShape.convertToLine();
(firstPoint, lastPoint) = snapLine(firstPoint, lastPoint);
log.info('Detected line: $firstPoint -> $lastPoint');
return Stroke(
strokeProperties: rawStroke.strokeProperties,
Expand Down Expand Up @@ -104,4 +106,26 @@ class ShapePen extends Pen {
..isComplete = true;
}
}

/// Snaps a line to either horizontal or vertical
/// if the angle is close enough.
static (Offset firstPoint, Offset lastPoint) snapLine(
Offset firstPoint,
Offset lastPoint,
) {
final dx = (lastPoint.dx - firstPoint.dx).abs();
final dy = (lastPoint.dy - firstPoint.dy).abs();
final angle = math.atan2(dy, dx);

const snapAngle = 5 * math.pi / 180; // 5 degrees
if (angle < snapAngle) {
// snap to horizontal
return (firstPoint, Offset(lastPoint.dx, firstPoint.dy));
} else if (angle > math.pi / 2 - snapAngle) {
// snap to vertical
return (firstPoint, Offset(firstPoint.dx, lastPoint.dy));
} else {
return (firstPoint, lastPoint);
}
}
}
49 changes: 49 additions & 0 deletions test/snap_line_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import 'dart:math' as math;

import 'package:flutter_test/flutter_test.dart';
import 'package:saber/data/tools/shape_pen.dart';

void main() {
group('ShapePen.snapLine', () {
test('horizontal line', () {
final (firstPoint, lastPoint) = ShapePen.snapLine(
Offset.zero,
const Offset(100, 1),
);
expect(firstPoint, Offset.zero);
expect(lastPoint, const Offset(100, 0));
});

test('horizontal line (under/over threshold)', () {
final threshold = 100 * math.tan(5 * math.pi / 180);
final underThreshold = ShapePen.snapLine(
Offset.zero,
Offset(100, threshold * 0.99),
);
final overThreshold = ShapePen.snapLine(
Offset.zero,
Offset(100, threshold * 1.01),
);
expect(underThreshold.$2.dy, 0);
expect(overThreshold.$2.dy, threshold * 1.01);
});

test('vertical line', () {
final (firstPoint, lastPoint) = ShapePen.snapLine(
Offset.zero,
const Offset(1, 100),
);
expect(firstPoint, Offset.zero);
expect(lastPoint, const Offset(0, 100));
});

test('diagonal line doesn\'t snap', () {
final (firstPoint, lastPoint) = ShapePen.snapLine(
Offset.zero,
const Offset(100, 101),
);
expect(firstPoint, Offset.zero);
expect(lastPoint, const Offset(100, 101));
});
});
}

0 comments on commit 5806b19

Please sign in to comment.