Skip to content

Commit

Permalink
Fix performance of path eraser, partially fix #528
Browse files Browse the repository at this point in the history
  • Loading branch information
CodeDoctorDE committed Feb 5, 2024
1 parent 9c72e6b commit 196eeb9
Show file tree
Hide file tree
Showing 3 changed files with 53 additions and 45 deletions.
64 changes: 32 additions & 32 deletions app/lib/handlers/eraser.dart
Original file line number Diff line number Diff line change
Expand Up @@ -37,48 +37,48 @@ class EraserHandler extends Handler<EraserTool> {

//method that handles the erasing logic. It determines whether an element should be erased based on its distance from the cursor and the stroke width of the eraser.
Future<void> _changeElement(Offset position, EventContext context) async {
final globalPos = context.getCameraTransform().localToGlobal(position);
final transform = context.getCameraTransform();
final globalPos = transform.localToGlobal(position);
final size = data.strokeWidth;
final shouldErase =
_lastErased == null || (globalPos - _lastErased!).distance > size;
final page = context.getPage();
if (page == null) return;
if (!_currentlyErasing && shouldErase) {
_currentlyErasing = true;
_lastErased = globalPos;
final ray = await rayCast(globalPos, context.getDocumentBloc(),
context.getCameraTransform(), size);
final elements = ray.map((e) => e.element).whereType<PenElement>();
final modified = <int, List<PadElement>>{};
for (final element in elements) {
List<List<PathPoint>> paths = [[]];
bool broken = false;
for (final point in element.points) {
if ((point.toOffset() - globalPos).distance >= (size * size)) {
paths.last.add(point);
continue;
} else {
if (paths.last.isNotEmpty) {
paths.add([]);
broken = true;
}
if (_currentlyErasing || !shouldErase) return;
_currentlyErasing = true;
_lastErased = globalPos;
final ray =
await rayCast(globalPos, context.getDocumentBloc(), transform, size);
final elements = ray.map((e) => e.element).whereType<PenElement>();
final modified = <int, List<PadElement>>{};
for (final element in elements) {
List<List<PathPoint>> paths = [[]];
bool broken = false;
for (final point in element.points) {
if ((point.toOffset() - globalPos).distance >= (size * size)) {
paths.last.add(point);
continue;
} else {
if (paths.last.isNotEmpty) {
paths.add([]);
broken = true;
}
}
final index = page.content.indexOf(element);
if (broken) {
modified[index] = [];
}
modified[index] = paths
.where((element) => element.isNotEmpty)
.map((e) => element.copyWith(points: e))
.toList();
}
if (modified.isNotEmpty) {
context.getDocumentBloc().add(ElementsChanged(modified));
await context.getDocumentBloc().stream.first;
final index = page.content.indexOf(element);
if (broken) {
modified[index] = [];
}
_currentlyErasing = false;
modified[index] = paths
.where((element) => element.isNotEmpty)
.map((e) => element.copyWith(points: e))
.toList();
}
if (modified.isNotEmpty) {
context.getDocumentBloc().add(ElementsChanged(modified));
await context.getDocumentBloc().stream.first;
}
_currentlyErasing = false;
}

// Called when the user releases the pointer. It completes the erasing action.
Expand Down
32 changes: 19 additions & 13 deletions app/lib/handlers/path_eraser.dart
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
part of 'handler.dart';

class PathEraserHandler extends Handler<PathEraserTool> {
bool _removeRunning = false;
Offset? _currentPos;
bool _currentlyErasing = false;
Offset? _currentPos, _lastErased;
PathEraserHandler(super.data);

@override
Expand All @@ -21,24 +21,30 @@ class PathEraserHandler extends Handler<PathEraserTool> {
context.refresh();
}

@override
void resetInput(DocumentBloc bloc) {
_currentPos = null;
}

@override
Future<void> onPointerMove(
PointerMoveEvent event, EventContext context) async {
_currentPos = event.localPosition;
context.refresh();
if (_removeRunning) return;
final transform = context.getCameraTransform();
_removeRunning = true;
final hits = await rayCast(
transform.localToGlobal(event.localPosition),
context.getDocumentBloc(),
context.getCameraTransform(),
data.strokeWidth / transform.size,
);
final globalPos = transform.localToGlobal(event.localPosition);
final size = data.strokeWidth;
final shouldErase =
_lastErased == null || (globalPos - _lastErased!).distance > size;
context.refresh();
if (_currentlyErasing || !shouldErase) return;
_currentlyErasing = true;
_lastErased = globalPos;
final ray =
await rayCast(globalPos, context.getDocumentBloc(), transform, size);
final page = context.getPage();
if (page == null) return;
final indexes = hits.map((e) => page.content.indexOf(e.element)).toList();
final indexes = ray.map((e) => page.content.indexOf(e.element)).toList();
context.addDocumentEvent(ElementsRemoved(indexes));
_removeRunning = false;
_currentlyErasing = false;
}
}
2 changes: 2 additions & 0 deletions fastlane/metadata/android/en-US/changelogs/91.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,7 @@
* Fix pdf.js on web
* Fix exporting on web
* Fix notch area not being used in full screen ([#368](https://github.com/LinwoodDev/Butterfly/issues/368))
* Fix performance of the eraser tool ([#461](https://github.com/LinwoodDev/Butterfly/issues/461))
* Fix performance of the path eraser tool (partially [#528](https://github.com/LinwoodDev/Butterfly/issues/528))

Read more here: https://linwood.dev/butterfly/2.0.3-rc.0

0 comments on commit 196eeb9

Please sign in to comment.