diff options
Diffstat (limited to 'src/client/views/InkStrokeProperties.ts')
-rw-r--r-- | src/client/views/InkStrokeProperties.ts | 85 |
1 files changed, 50 insertions, 35 deletions
diff --git a/src/client/views/InkStrokeProperties.ts b/src/client/views/InkStrokeProperties.ts index 52ea89cde..3920ecc2a 100644 --- a/src/client/views/InkStrokeProperties.ts +++ b/src/client/views/InkStrokeProperties.ts @@ -1,20 +1,22 @@ import { Bezier } from 'bezier-js'; +import * as _ from 'lodash'; import { action, makeObservable, observable, reaction, runInAction } from 'mobx'; import { Doc, NumListCast, Opt } from '../../fields/Doc'; -import { InkData, InkField, InkTool, PointData } from '../../fields/InkField'; +import { InkData, InkField, InkTool } from '../../fields/InkField'; import { List } from '../../fields/List'; import { listSpec } from '../../fields/Schema'; import { Cast, NumCast } from '../../fields/Types'; +import { PointData } from '../../pen-gestures/GestureTypes'; import { Point } from '../../pen-gestures/ndollar'; import { DocumentType } from '../documents/DocumentTypes'; -import { FitOneCurve } from '../util/bezierFit'; -import { DocumentManager } from '../util/DocumentManager'; import { undoBatch } from '../util/UndoManager'; +import { FitOneCurve } from '../util/bezierFit'; import { InkingStroke } from './InkingStroke'; +import { CollectionFreeFormView } from './collections/collectionFreeForm'; import { DocumentView } from './nodes/DocumentView'; -import * as _ from 'lodash'; export class InkStrokeProperties { + // eslint-disable-next-line no-use-before-define static _Instance: InkStrokeProperties | undefined; public static get Instance() { return this._Instance || new InkStrokeProperties(); @@ -28,11 +30,15 @@ export class InkStrokeProperties { makeObservable(this); reaction( () => this._controlButton, - button => button && (Doc.ActiveTool = InkTool.None) + button => { + button && (Doc.ActiveTool = InkTool.None); + } ); reaction( () => Doc.ActiveTool, - tool => tool !== InkTool.None && (this._controlButton = false) + tool => { + tool !== InkTool.None && (this._controlButton = false); + } ); } @@ -46,7 +52,7 @@ export class InkStrokeProperties { func: (view: DocumentView, ink: InkData, ptsXscale: number, ptsYscale: number, inkStrokeWidth: number) => { X: number; Y: number }[] | undefined, requireCurrPoint: boolean = false ) => { - var appliedFunc = false; + let appliedFunc = false; (strokes instanceof DocumentView ? [strokes] : strokes)?.forEach( action(inkView => { if (!requireCurrPoint || this._currentPoint !== -1) { @@ -85,7 +91,7 @@ export class InkStrokeProperties { */ @undoBatch addPoints = (inkView: DocumentView, t: number, i: number, controls: { X: number; Y: number }[]) => { - this.applyFunction(inkView, (view: DocumentView, ink: InkData) => { + this.applyFunction(inkView, (view: DocumentView /* , ink: InkData */) => { const doc = view.Document; const array = [controls[i], controls[i + 1], controls[i + 2], controls[i + 3]]; const newsegs = new Bezier(array.map(p => ({ x: p.X, y: p.Y }))).split(t); @@ -94,7 +100,9 @@ export class InkStrokeProperties { // Updating the indices of the control points whose handle tangency has been broken. doc.brokenInkIndices = new List(Cast(doc.brokenInkIndices, listSpec('number'), []).map(control => (control > i ? control + 4 : control))); - runInAction(() => (this._currentPoint = -1)); + runInAction(() => { + this._currentPoint = -1; + }); return controls; }); @@ -126,8 +134,8 @@ export class InkStrokeProperties { */ getNewHandlePoints = (C: PointData[], D: PointData[], newControl: PointData) => { const [m, n] = [C.length, D.length]; - let handleSizeA = Math.sqrt(Math.pow(newControl.X - C[0].X, 2) + Math.pow(newControl.Y - C[0].Y, 2)); - let handleSizeB = Math.sqrt(Math.pow(D[n - 1].X - newControl.X, 2) + Math.pow(D[n - 1].Y - newControl.Y, 2)); + let handleSizeA = Math.sqrt((newControl.X - C[0].X) ** 2 + (newControl.Y - C[0].Y) ** 2); + let handleSizeB = Math.sqrt((D[n - 1].X - newControl.X) ** 2 + (D[n - 1].Y - newControl.Y) ** 2); // Scaling adjustments to improve the ratio between the magnitudes of the two handle lines. // (Ensures that the new point added doesn't augment the inital shape of the curve much). if (handleSizeA < 75 && handleSizeB < 75) { @@ -167,13 +175,13 @@ export class InkStrokeProperties { const start = this._currentPoint === 0 ? 0 : this._currentPoint - 4; const splicedPoints = ink.slice(start, start + (this._currentPoint === 0 || this._currentPoint === ink.length - 1 ? 4 : 8)); const samples: Point[] = []; - var startDir = { x: 0, y: 0 }; - var endDir = { x: 0, y: 0 }; - for (var i = 0; i < splicedPoints.length / 4; i++) { + let startDir = { x: 0, y: 0 }; + let endDir = { x: 0, y: 0 }; + for (let i = 0; i < splicedPoints.length / 4; i++) { const bez = new Bezier(splicedPoints.slice(i * 4, i * 4 + 4).map(p => ({ x: p.X, y: p.Y }))); if (i === 0) startDir = bez.derivative(0); if (i === splicedPoints.length / 4 - 1) endDir = bez.derivative(1); - for (var t = 0; t < (i === splicedPoints.length / 4 - 1 ? 1 + 1e-7 : 1); t += 0.05) { + for (let t = 0; t < (i === splicedPoints.length / 4 - 1 ? 1 + 1e-7 : 1); t += 0.05) { const pt = bez.compute(t); samples.push(new Point(pt.x, pt.y)); } @@ -186,7 +194,9 @@ export class InkStrokeProperties { } } doc.brokenInkIndices = new List(brokenIndices.map(control => (control >= this._currentPoint ? control - 4 : control))); - runInAction(() => (this._currentPoint = -1)); + runInAction(() => { + this._currentPoint = -1; + }); return newPoints.length < 4 ? undefined : newPoints; }, true @@ -200,7 +210,7 @@ export class InkStrokeProperties { */ @undoBatch rotateInk = (inkStrokes: DocumentView[], angle: number, scrpt: PointData) => { - this.applyFunction(inkStrokes, (view: DocumentView, ink: InkData, xScale: number, yScale: number, inkStrokeWidth: number) => { + this.applyFunction(inkStrokes, (view: DocumentView, ink: InkData, xScale: number, yScale: number /* , inkStrokeWidth: number */) => { const inkCenterPt = view.ComponentView?.ptFromScreen?.(scrpt); return !inkCenterPt ? ink @@ -247,37 +257,37 @@ export class InkStrokeProperties { const closed = InkingStroke.IsClosed(ink); const brokenIndices = Cast(inkView.Document.brokenInkIndices, listSpec('number'), []); if (origInk && this._currentPoint > 0 && this._currentPoint < ink.length - 1 && brokenIndices.findIndex(value => value === controlIndex) === -1) { - const cpt_before = ink[controlIndex]; - const cpt = { X: cpt_before.X + deltaX, Y: cpt_before.Y + deltaY }; + const cptBefore = ink[controlIndex]; + const cpt = { X: cptBefore.X + deltaX, Y: cptBefore.Y + deltaY }; const newink = origInk.slice(); const start = this._currentPoint === 0 ? 0 : this._currentPoint - 4; const splicedPoints = origInk.slice(start, start + (this._currentPoint === 0 || this._currentPoint === ink.length - 1 ? 4 : 8)); const { nearestT, nearestSeg } = InkStrokeProperties.nearestPtToStroke(splicedPoints, cpt); - if ((nearestSeg === 0 && nearestT < 1e-1) || (nearestSeg === 4 && 1 - nearestT < 1e-1)) return ink.slice(); + if ((nearestSeg === 0 && nearestT < 1e-1) || (nearestSeg === 4 && 1 - nearestT < 1e-1) || nearestSeg < 0) return ink.slice(); const samplesLeft: Point[] = []; const samplesRight: Point[] = []; - var startDir = { x: 0, y: 0 }; - var endDir = { x: 0, y: 0 }; - for (var i = 0; i < nearestSeg / 4 + 1; i++) { + let startDir = { x: 0, y: 0 }; + let endDir = { x: 0, y: 0 }; + for (let i = 0; i < nearestSeg / 4 + 1; i++) { const bez = new Bezier(splicedPoints.slice(i * 4, i * 4 + 4).map(p => ({ x: p.X, y: p.Y }))); if (i === 0) startDir = bez.derivative(_.isEqual(bez.derivative(0), { x: 0, y: 0, t: 0 }) ? 1e-8 : 0); if (i === nearestSeg / 4) endDir = bez.derivative(nearestT); - for (var t = 0; t < (i === nearestSeg / 4 ? nearestT + 0.05 : 1); t += 0.05) { + for (let t = 0; t < (i === nearestSeg / 4 ? nearestT + 0.05 : 1); t += 0.05) { const pt = bez.compute(i !== nearestSeg / 4 ? t : Math.min(nearestT, t)); samplesLeft.push(new Point(pt.x, pt.y)); } } - var { finalCtrls } = FitOneCurve(samplesLeft, { X: startDir.x, Y: startDir.y }, { X: endDir.x, Y: endDir.y }); - for (var i = nearestSeg / 4; i < splicedPoints.length / 4; i++) { + let { finalCtrls } = FitOneCurve(samplesLeft, { X: startDir.x, Y: startDir.y }, { X: endDir.x, Y: endDir.y }); + for (let i = nearestSeg / 4; i < splicedPoints.length / 4; i++) { const bez = new Bezier(splicedPoints.slice(i * 4, i * 4 + 4).map(p => ({ x: p.X, y: p.Y }))); if (i === nearestSeg / 4) startDir = bez.derivative(nearestT); if (i === splicedPoints.length / 4 - 1) endDir = bez.derivative(_.isEqual(bez.derivative(1), { x: 0, y: 0, t: 1 }) ? 1 - 1e-8 : 1); - for (var t = i === nearestSeg / 4 ? nearestT : 0; t < (i === nearestSeg / 4 ? 1 + 0.05 + 1e-7 : 1 + 1e-7); t += 0.05) { + for (let t = i === nearestSeg / 4 ? nearestT : 0; t < (i === nearestSeg / 4 ? 1 + 0.05 + 1e-7 : 1 + 1e-7); t += 0.05) { const pt = bez.compute(Math.min(1, t)); samplesRight.push(new Point(pt.x, pt.y)); } } - const { finalCtrls: rightCtrls, error: errorRight } = FitOneCurve(samplesRight, { X: startDir.x, Y: startDir.y }, { X: endDir.x, Y: endDir.y }); + const { finalCtrls: rightCtrls /* , error: errorRight */ } = FitOneCurve(samplesRight, { X: startDir.x, Y: startDir.y }, { X: endDir.x, Y: endDir.y }); finalCtrls = finalCtrls.concat(rightCtrls); newink.splice(this._currentPoint - 4, 8, ...finalCtrls); return newink; @@ -307,11 +317,12 @@ export class InkStrokeProperties { }); public static nearestPtToStroke(ctrlPoints: { X: number; Y: number }[], refInkSpacePt: { X: number; Y: number }, excludeSegs?: number[]) { - var distance = Number.MAX_SAFE_INTEGER; - var nearestT = -1; - var nearestSeg = -1; - var nearestPt = { X: 0, Y: 0 }; - for (var i = 0; i < ctrlPoints.length - 3; i += 4) { + let distance = Number.MAX_SAFE_INTEGER; + let nearestT = -1; + 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 }); @@ -369,17 +380,18 @@ export class InkStrokeProperties { }; snapToAllCurves = (screenDragPt: { X: number; Y: number }, inkView: DocumentView, snapData: { nearestPt: { X: number; Y: number }; distance: number }, ink: InkData, controlIndex: number) => { - const containingCollection = inkView.CollectionFreeFormView; + const containingCollection = CollectionFreeFormView.from(inkView); const containingDocView = containingCollection?.DocumentView?.(); containingCollection?.childDocs .filter(doc => doc.type === DocumentType.INK) .forEach(doc => { - const testInkView = DocumentManager.Instance.getDocumentView(doc, containingDocView); + const testInkView = DocumentView.getDocumentView(doc, containingDocView); const snapped = testInkView?.ComponentView?.snapPt?.(screenDragPt, doc === inkView.Document ? this.excludeSelfSnapSegs(ink, controlIndex) : []); if (snapped && snapped.distance < snapData.distance) { const snappedInkPt = doc === inkView.Document ? snapped.nearestPt : inkView.ComponentView?.ptFromScreen?.(testInkView?.ComponentView?.ptToScreen?.(snapped.nearestPt) ?? { X: 0, Y: 0 }); // convert from snapped ink coordinate system to dragged ink coordinate system by converting to/from screen space if (snappedInkPt) { + // eslint-disable-next-line no-param-reassign snapData = { nearestPt: snappedInkPt, distance: snapped.distance }; } } @@ -406,6 +418,7 @@ export class InkStrokeProperties { inkCopy[handleIndexB] = this.rotatePoint(handleB, controlPoint, angleDifference); return inkCopy; } + return undefined; }); }; @@ -430,7 +443,9 @@ export class InkStrokeProperties { const magnitudeB = Math.sqrt(vectorB.X * vectorB.X + vectorB.Y * vectorB.Y); if (magnitudeA === 0 || magnitudeB === 0) return 0; // Normalizing the vectors. + // eslint-disable-next-line no-param-reassign vectorA = { X: vectorA.X / magnitudeA, Y: vectorA.Y / magnitudeA }; + // eslint-disable-next-line no-param-reassign vectorB = { X: vectorB.X / magnitudeB, Y: vectorB.Y / magnitudeB }; return Math.acos(vectorB.X * vectorA.X + vectorB.Y * vectorA.Y); } |