Skip to content

Commit

Permalink
Improvements to drawing mode
Browse files Browse the repository at this point in the history
  • Loading branch information
Zverik committed Mar 30, 2024
1 parent 64bc397 commit 0bf5a50
Show file tree
Hide file tree
Showing 11 changed files with 254 additions and 47 deletions.
3 changes: 3 additions & 0 deletions lib/helpers/draw_style.dart
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ class DrawingStyle {

const kTypeStyles = <String, DrawingStyle>{
"scribble": DrawingStyle(color: Colors.white, thin: true),
"eraser": DrawingStyle(color: Colors.black54),
"road": DrawingStyle(color: Colors.white70),
"track": DrawingStyle(color: Colors.white70, dashed: true),
"footway": DrawingStyle(color: Colors.red),
Expand All @@ -52,6 +53,8 @@ final kTypeStylesReversed =
kTypeStyles.map((key, value) => MapEntry(value, key));

const kUnknownStyle = DrawingStyle(color: Colors.grey);
const kToolEraser = "eraser";
const kToolScribble = "scribble";

const kStyleIcons = <String, IconData>{
"eraser": Icons.cleaning_services,
Expand Down
75 changes: 68 additions & 7 deletions lib/helpers/geometry.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import 'dart:math' show sqrt, pow;

import 'package:every_door/helpers/equirectangular.dart';
import 'package:latlong2/latlong.dart' show LatLng;
import 'package:flutter_map/flutter_map.dart' show LatLngBounds;
Expand Down Expand Up @@ -191,15 +193,19 @@ class MultiPolygon implements Polygon {
}

class LineString extends Geometry {
final List<LatLng> _nodes;
final List<LatLng> nodes;
LatLngBounds? _cachedBounds;

LineString(Iterable<LatLng> nodes) : _nodes = List.of(nodes) {
if (_nodes.length < 2)
LineString(Iterable<LatLng> nodes) : nodes = List.of(nodes, growable: false) {
if (this.nodes.length < 2)
throw GeometryException('A path must have at least two nodes.');
}

@override
LatLngBounds get bounds => LatLngBounds.fromPoints(_nodes);
LatLngBounds get bounds {
_cachedBounds ??= LatLngBounds.fromPoints(nodes);
return _cachedBounds!;
}

@override
// TODO: proper center on the line
Expand All @@ -208,12 +214,67 @@ class LineString extends Geometry {
double getLengthInMeters() {
double length = 0.0;
final distance = DistanceEquirectangular();
for (int i = 1; i < _nodes.length; i++) {
length += distance(_nodes[i - 1], _nodes[i]);
for (int i = 1; i < nodes.length; i++) {
length += distance(nodes[i - 1], nodes[i]);
}
return length;
}

LatLng closestPoint(LatLng point) {
num minDist = double.infinity;
LatLng point = nodes[0];

for (int i = 1; i < nodes.length; i++) {
final x = point.longitude;
final y = point.latitude;
final x1 = nodes[i - 1].longitude;
final y1 = nodes[i - 1].latitude;
final x2 = nodes[i].longitude;
final y2 = nodes[i].latitude;
final dx = x2 - x1;
final dy = y2 - y1;

final dot = (x - x1) * dx + (y - y1) * dy;
final len = dx * dx + dy * dy;
double t = -1;
if (len > 0.0) t = dot / len;

double ix;
double iy;
if (t <= 0)
(ix, iy) = (x1, y1);
else if (t >= 1)
(ix, iy) = (x2, y2);
else
(ix, iy) = (x1 + t * dx, y1 + t * dy);

final d = pow(x - ix, 2) + pow(y - iy, 2);
if (d < minDist) {
minDist = d;
point = LatLng(iy, ix);
}
}

return point;
}

double distanceToPoint(LatLng point, {bool inMeters = true}) {
// I am lazy hence this is a StackOverflow-inspired code.
final closest = closestPoint(point);
if (inMeters) {
final distance = DistanceEquirectangular();
return distance(point, closest);
} else {
return sqrt(pow(point.longitude - closest.longitude, 2) +
pow(point.latitude - closest.latitude, 2));
}
}

bool intersects(LineString other) {
if (!bounds.isOverlapping(other.bounds)) return false;
return true; // TODO
}

@override
String toString() => 'LineString($_nodes)';
String toString() => 'LineString($nodes)';
}
12 changes: 6 additions & 6 deletions lib/models/note.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import 'package:every_door/constants.dart';
import 'package:every_door/helpers/draw_style.dart';
import 'package:every_door/helpers/geometry.dart';
import 'package:latlong2/latlong.dart' show LatLng;
import 'package:proximity_hash/geohash.dart';
import 'dart:convert' show json;
Expand Down Expand Up @@ -218,18 +219,18 @@ class OsmNote extends BaseNote {

class MapDrawing extends BaseNote {
static const dbType = 3;
final List<LatLng> coordinates;
final LineString path;
final String? author;
final String pathType;

MapDrawing({
super.id,
required this.coordinates,
required this.path,
required this.pathType,
this.author,
super.created,
super.deleting,
}) : super(type: dbType, location: coordinates[coordinates.length >> 1]);
}) : super(type: dbType, location: path.nodes[path.nodes.length >> 1]);

DrawingStyle get style => kTypeStyles[pathType] ?? kUnknownStyle;

Expand All @@ -244,7 +245,7 @@ class MapDrawing extends BaseNote {
id: data['id'],
pathType: data['path_type'],
author: data['author'],
coordinates: coords,
path: LineString(coords),
created: DateTime.fromMillisecondsSinceEpoch(data['created']),
deleting: data['is_deleting'] == 1,
);
Expand All @@ -256,8 +257,7 @@ class MapDrawing extends BaseNote {
...super.toJson(),
'path_type': pathType,
'author': author,
'coords':
coordinates.map((c) => '${c.latitude};${c.longitude}').join('|'),
'coords': path.nodes.map((c) => '${c.latitude};${c.longitude}').join('|'),
};
}

Expand Down
13 changes: 9 additions & 4 deletions lib/providers/notes.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import 'dart:ui';
import 'package:every_door/constants.dart';
import 'package:every_door/helpers/circle_bounds.dart';
import 'package:every_door/helpers/draw_style.dart';
import 'package:every_door/helpers/geometry.dart';
import 'package:every_door/helpers/osm_api_converters.dart';
import 'package:every_door/models/note.dart';
import 'package:every_door/providers/api_status.dart';
Expand All @@ -25,13 +26,14 @@ final notesProvider = ChangeNotifierProvider((ref) => NotesProvider(ref));
final ownScribblesProvider =
StateNotifierProvider<OwnScribblesController, bool>(
(_) => OwnScribblesController());
final currentPaintToolProvider = StateProvider<String>((_) => kToolScribble);

class NotesProvider extends ChangeNotifier {
static final _logger = Logger('NotesProvider');

final Ref _ref;
int length = 0;
List<(bool deleted, BaseNote note)> _undoStack = [];
final List<(bool deleted, MapDrawing note)> _undoStack = [];
int _undoStackLast = 0;

bool get haveChanges => length > 0;
Expand Down Expand Up @@ -191,7 +193,7 @@ class NotesProvider extends ChangeNotifier {
notes.add(MapDrawing(
id: noteData['id'],
author: noteData['username'],
coordinates: points.map((ll) => LatLng(ll[1], ll[0])).toList(),
path: LineString(points.map((ll) => LatLng(ll[1], ll[0]))),
pathType: noteData['style'],
));
} else if (noteData.containsKey('location')) {
Expand Down Expand Up @@ -251,7 +253,7 @@ class NotesProvider extends ChangeNotifier {
'color': _colorToHex(note.style.color),
'dashed': note.style.dashed,
'thin': note.style.stroke < DrawingStyle.kDefaultStroke,
'points': note.coordinates
'points': note.path.nodes
.map((ll) => [ll.longitude, ll.latitude])
.toList(),
});
Expand Down Expand Up @@ -427,6 +429,9 @@ class NotesProvider extends ChangeNotifier {
Future<void> deleteNote(BaseNote note,
{bool notify = true, bool addUndo = true}) async {
final database = await _ref.read(databaseProvider).database;
// TODO: NOOOOO THIS DELETES FROM DATABASE
// TODO: Set deletion flag and do this properly. Also rethink the undo stack.
// TODO: Undo stack should group deletions.
await database.delete(
BaseNote.kTableName,
where: 'id = ?',
Expand All @@ -440,7 +445,7 @@ class NotesProvider extends ChangeNotifier {
bool get redoIsEmpty => _undoStackLast >= _undoStack.length;

_addToUndoStack(BaseNote note, bool deleted) {
if (note is OsmNote) return;
if (note is !MapDrawing) return;
// Add it to undo stack, discarding the top if needed.
if (_undoStackLast < _undoStack.length)
_undoStack.removeRange(_undoStackLast, _undoStack.length);
Expand Down
2 changes: 1 addition & 1 deletion lib/screens/editor/map_chooser.dart
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ class _MapChooserPageState extends ConsumerState<MapChooserPage> {
polylines: [
for (final drawing in nearestNotes.whereType<MapDrawing>())
Polyline(
points: drawing.coordinates,
points: drawing.path.nodes,
color: drawing.style.color,
strokeWidth: drawing.style.stroke / 3,
isDotted: drawing.style.dashed,
Expand Down
10 changes: 10 additions & 0 deletions lib/screens/editor/note.dart
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,16 @@ class _NoteEditorPaneState extends ConsumerState<NoteEditorPane> {
message = value.trim();
},
),
if (widget.note == null)
SwitchListTile(
value: isOsmNote,
title: Text('Publish to OSM'),
onChanged: (value) {
setState(() {
isOsmNote = !isOsmNote;
});
},
),
Row(
children: [
if (widget.note != null)
Expand Down

0 comments on commit 0bf5a50

Please sign in to comment.