Skip to content

Commit

Permalink
Add line snapping
Browse files Browse the repository at this point in the history
  • Loading branch information
twangodev committed Jul 30, 2024
1 parent 1d98cce commit 4283af8
Show file tree
Hide file tree
Showing 6 changed files with 95 additions and 15 deletions.
1 change: 1 addition & 0 deletions lib/models/snap.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
// TODO snap candidates
1 change: 1 addition & 0 deletions lib/widgets/editor/editor_config.dart
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ class EditorConfig {
// Painter Configuration

static const crossMarkerSize = 25.0;
static const crossXMarkerSize = 15.0;

static const minorGridSize = 20.0;
static const majorGridDensity = 5; // 100.0 is the majorGridSize
Expand Down
39 changes: 39 additions & 0 deletions lib/widgets/editor/editor_logic.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import 'dart:ui';

import 'package:vector_math/vector_math_64.dart';

class EditorLogic {
static bool interceptsSquare(Offset parent, Offset child, double size) {
Rect rect = Rect.fromCenter(center: parent, width: size, height: size);
return rect.contains(child);
}

static bool interceptsCircle(Offset parent, Offset child, double radius) {
double distance = (parent - child).distanceSquared;
return distance < radius * radius;
}

static Offset nearestPointOnLine(Offset start, Offset end, Offset offset) {
final Vector2 startVector = Vector2(start.dx, start.dy);
final Vector2 endVector = Vector2(end.dx, end.dy);
final Vector2 pointVector = Vector2(offset.dx, offset.dy);

final Vector2 lineVector = endVector - startVector;

final Vector2 startToPointVector = pointVector - startVector;

final double lineLengthSquared = lineVector.length2;
final double t = (lineVector.dot(startToPointVector)) / lineLengthSquared;

Vector2 nearestPoint;
if (t < 0) {
nearestPoint = startVector;
} else if (t > 1) {
nearestPoint = endVector;
} else {
nearestPoint = startVector + lineVector * t;
}

return Offset(nearestPoint.x, nearestPoint.y);
}
}
20 changes: 20 additions & 0 deletions lib/widgets/editor/editor_painter.dart
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,20 @@ class EditorPainter extends CustomPainter {
canvas.drawRect(rect, snapPaint);
}

void drawCrossXMarker(Canvas canvas, Offset offset) {
final Paint crossPaint = Paint()
..color = Colors.blue
..strokeWidth = 1.5 * scaleInverse
..style = PaintingStyle.stroke;

double halfSize = EditorConfig.crossXMarkerSize * scaleInverse / 2;

canvas.drawLine(offset.translate(-halfSize, -halfSize),
offset.translate(halfSize, halfSize), crossPaint);
canvas.drawLine(offset.translate(-halfSize, halfSize),
offset.translate(halfSize, -halfSize), crossPaint);
}

void establishGridSnap(Canvas canvas) {
if (activeShouldSnapToGrid!) {
drawSnapMarker(canvas, activePointerGridSnap!);
Expand All @@ -203,6 +217,12 @@ class EditorPainter extends CustomPainter {
return;
}

Offset? nearestLineSnap = editorData.nearestLineSnap(activePointer!);
if (nearestLineSnap != null) {
drawCrossXMarker(canvas, nearestLineSnap);
return;
}

establishGridSnap(canvas);
}

Expand Down
36 changes: 34 additions & 2 deletions lib/widgets/editor/editor_painter_data.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ import 'package:flutter/material.dart';
import 'package:logging/logging.dart';
import 'package:toolfoam/models/tools/tf_tool_data.dart';
import 'package:toolfoam/widgets/editor/editor_config.dart';
import 'package:toolfoam/widgets/editor/editor_util.dart';

import '../../models/line.dart';
import 'editor_logic.dart';

class EditorData extends ChangeNotifier {
EditorData({
Expand Down Expand Up @@ -72,9 +74,35 @@ class EditorData extends ChangeNotifier {
return shouldSnapToGrid(activePointer!);
}

Offset? nearestLineSnap(Offset offset, {String? ignoreUuid}) {
Offset? nearestLineSnap;
double nearestDistance = double.infinity;
for (Line line in toolData.lines) {
if (line.point1 == ignoreUuid || line.point2 == ignoreUuid) continue;

Offset start = toolData.points[line.point1]!;
Offset end = toolData.points[line.point2]!;

Offset nearestPoint = EditorLogic.nearestPointOnLine(start, end, offset);
Offset delta = nearestPoint - offset;
double distance = delta.distanceSquared;

bool isNearest = distance < nearestDistance;
double threshold = EditorConfig.defaultSnapTolerance / 2 * scaleInverse;
bool isSnap = distance < threshold * threshold;

if (isNearest && isSnap) {
nearestLineSnap = nearestPoint;
nearestDistance = distance;
}
}

return nearestLineSnap;
}

MapEntry<String, Offset>? nearestPointSnap(
Offset offset, String? ignoreUuid) {
MapEntry<String, Offset>? nearestPointSnap = null;
MapEntry<String, Offset>? nearestPointSnap;
double nearestDistance = double.infinity;
for (MapEntry<String, Offset> entry in toolData.points.entries) {
if (entry.key == ignoreUuid) continue;
Expand All @@ -99,6 +127,10 @@ class EditorData extends ChangeNotifier {
Offset effectivePointerCoordinates(Offset offset, {String? ignoreUuid}) {
MapEntry<String, Offset>? pointSnap = nearestPointSnap(offset, ignoreUuid);
if (pointSnap != null) return pointSnap.value;

Offset? lineSnap = nearestLineSnap(offset);
if (lineSnap != null) return lineSnap;

if (shouldSnapToGrid(offset)) return gridSnap(offset);

return offset;
Expand Down
13 changes: 0 additions & 13 deletions lib/widgets/editor/editor_util.dart

This file was deleted.

0 comments on commit 4283af8

Please sign in to comment.