diff options
| author | bobzel <zzzman@gmail.com> | 2024-10-01 19:03:05 -0400 |
|---|---|---|
| committer | bobzel <zzzman@gmail.com> | 2024-10-01 19:03:05 -0400 |
| commit | 3d6c5b151cebc74df4b8fc79e5bb755c51ad65a2 (patch) | |
| tree | 9440404e1399f2ca668527366c99654f42980576 /src/client/views/InkStrokeProperties.ts | |
| parent | 5d859cab5fa714860723fa252498c407d5909cdc (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.ts | 49 |
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'); |
