aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/InkStrokeProperties.ts
diff options
context:
space:
mode:
authorbobzel <zzzman@gmail.com>2024-10-01 19:03:05 -0400
committerbobzel <zzzman@gmail.com>2024-10-01 19:03:05 -0400
commit3d6c5b151cebc74df4b8fc79e5bb755c51ad65a2 (patch)
tree9440404e1399f2ca668527366c99654f42980576 /src/client/views/InkStrokeProperties.ts
parent5d859cab5fa714860723fa252498c407d5909cdc (diff)
changed how smoothing curves works - got rid of simplify.js dependency
Diffstat (limited to 'src/client/views/InkStrokeProperties.ts')
-rw-r--r--src/client/views/InkStrokeProperties.ts49
1 files changed, 25 insertions, 24 deletions
diff --git a/src/client/views/InkStrokeProperties.ts b/src/client/views/InkStrokeProperties.ts
index 5cacde0d4..358274f0e 100644
--- a/src/client/views/InkStrokeProperties.ts
+++ b/src/client/views/InkStrokeProperties.ts
@@ -1,19 +1,18 @@
import { Bezier } from 'bezier-js';
+import * as fitCurve from 'fit-curve';
import * as _ from 'lodash';
import { action, makeObservable, observable, reaction, runInAction } from 'mobx';
-import simplify from 'simplify-js';
import { Doc, NumListCast, Opt } from '../../fields/Doc';
+import { DocData } from '../../fields/DocSymbols';
import { InkData, InkField, InkTool } from '../../fields/InkField';
import { List } from '../../fields/List';
import { listSpec } from '../../fields/Schema';
import { Cast, NumCast } from '../../fields/Types';
-import { Gestures, PointData } from '../../pen-gestures/GestureTypes';
-import { GestureUtils } from '../../pen-gestures/GestureUtils';
+import { PointData } from '../../pen-gestures/GestureTypes';
import { Point } from '../../pen-gestures/ndollar';
import { DocumentType } from '../documents/DocumentTypes';
import { undoable } from '../util/UndoManager';
import { FitOneCurve } from '../util/bezierFit';
-import { GestureOverlay } from './GestureOverlay';
import { InkingStroke } from './InkingStroke';
import { CollectionFreeFormView } from './collections/collectionFreeForm';
import { DocumentView } from './nodes/DocumentView';
@@ -322,7 +321,6 @@ export class InkStrokeProperties {
let nearestSeg = -1;
let nearestPt = { X: 0, Y: 0 };
for (let i = 0; i < ctrlPoints.length - 3; i += 4) {
- // eslint-disable-next-line no-continue
if (excludeSegs?.includes(i)) continue;
const array = [ctrlPoints[i], ctrlPoints[i + 1], ctrlPoints[i + 2], ctrlPoints[i + 3]];
const point = new Bezier(array.map(p => ({ x: p.X, y: p.Y }))).project({ x: refInkSpacePt.X, y: refInkSpacePt.Y });
@@ -488,31 +486,34 @@ export class InkStrokeProperties {
});
}, 'move ink tangent');
+ sampleBezier = (curves: InkData) => {
+ const polylinePoints = [{ x: curves[0].X, y: curves[0].Y }];
+ for (let i = 0; i < curves.length / 4; i++) {
+ const bez = new Bezier(curves.slice(i * 4, i * 4 + 4).map(p => ({ x: p.X, y: p.Y })));
+ for (let t = 0.05; t < 1; t += 0.05) {
+ polylinePoints.push(bez.compute(t));
+ }
+ polylinePoints.push(bez.points[3]);
+ }
+ return polylinePoints.length > 2 ? polylinePoints : undefined;
+ };
/**
- * Function that "smooths" ink strokes by using the gesture recognizer to detect shapes and
- * removing excess control points with the simplify-js package.
+ * Function that "smooths" ink strokes by sampling the curve, then fitting it with new bezier curves, subject to a
+ * maximum pixel error tolerance
* @param inkDocs
- * @param tolerance Determines how strong the smooth effect will be
+ * @param tolerance how many pixels of error are allowed
*/
- smoothInkStrokes = undoable((inkDocs: Doc[], tolerance: number = 5) => {
+ smoothInkStrokes = undoable((inkDocs: Doc[], tolerance = 5) => {
inkDocs.forEach(inkDoc => {
const inkView = DocumentView.getDocumentView(inkDoc);
const inkStroke = inkView?.ComponentView as InkingStroke;
- const { inkData } = inkStroke.inkScaledData();
- const polylinePoints = inkData.filter((pt, index) => { return index % 4 === 0 || pt === inkData.lastElement()}).map(pt => { return { x: pt.X, y: pt.Y }; }); // prettier-ignore
- if (polylinePoints.length > 2) {
- const toKeep = simplify(polylinePoints, tolerance).map(pt => {return { X: pt.x, Y: pt.y }}); // prettier-ignore
- for (var i = 4; i < inkData.length - 3; i += 4) {
- const contains = toKeep.find(pt => pt.X === inkData[i].X && pt.Y === inkData[i].Y);
- if (!contains) {
- this._currentPoint = i;
- inkView && this.deletePoints(inkView, false);
- }
- }
- // close the curve if the first and last points are really close (based on tolerance)
- if (!InkingStroke.IsClosed(inkData) && Math.sqrt((inkData.lastElement().X - inkData[0].X) ** 2 + (inkData.lastElement().Y - inkData[0].Y) ** 2) <= tolerance * 2) {
- inkData[inkData.length - 1] = inkData[0];
- }
+ const polylinePoints = this.sampleBezier(inkStroke?.inkScaledData().inkData ?? [])?.map(pt => [pt.x, pt.y]);
+ if (polylinePoints) {
+ inkDoc[DocData].stroke = new InkField(
+ fitCurve.default(polylinePoints, tolerance)
+ .reduce((cpts, bez) =>
+ ({n: cpts.push(...bez.map(cpt => ({X:cpt[0], Y:cpt[1]}))), cpts}).cpts,
+ [] as {X:number, Y:number}[])); // prettier-ignore
}
});
}, 'smooth ink stroke');