From 9808c01be554ab210083af9c1ef7fd45fbd84c19 Mon Sep 17 00:00:00 2001 From: Zachary Zhang Date: Wed, 25 Sep 2024 21:26:31 -0400 Subject: fix scribble gesture to use bezier curve intersections --- src/client/views/GestureOverlay.tsx | 196 ++++++++++----------- .../collectionFreeForm/CollectionFreeFormView.tsx | 1 - 2 files changed, 91 insertions(+), 106 deletions(-) (limited to 'src') diff --git a/src/client/views/GestureOverlay.tsx b/src/client/views/GestureOverlay.tsx index 935eeb251..7e396d475 100644 --- a/src/client/views/GestureOverlay.tsx +++ b/src/client/views/GestureOverlay.tsx @@ -1,4 +1,5 @@ import * as fitCurve from 'fit-curve'; +import { Bezier } from 'bezier-js'; import { action, computed, makeObservable, observable, runInAction } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; @@ -95,7 +96,6 @@ export class GestureOverlay extends ObservableReactComponent { if (!(e.target as HTMLElement)?.className?.toString().startsWith('lm_')) { @@ -139,52 +139,52 @@ export class GestureOverlay extends ObservableReactComponent view.ComponentView instanceof CollectionFreeFormView); - const cuspArray = this.getCusps(); - const cuspBooleanArray: boolean[] = []; + console.log(inkData.map(ink => ({ x: ink.X, y: ink.Y }))); + let intersectArray: boolean[] = []; + const cuspArray = this.getCusps(inkData); + console.log(cuspArray.length); + for (let i = 0; i < cuspArray.length; i++) { + intersectArray[i] = false; + } const docsToDelete: Doc[] = []; - const childDocs = (ffView?.ComponentView as CollectionFreeFormView).childDocs; + const childDocs = (ffView?.ComponentView as CollectionFreeFormView).childDocs.slice(0, -1); childDocs.filter(doc => doc.type === 'ink').map(doc => DocumentView.getDocumentView(doc, DocumentView.getDocumentView(doc))); if ((ffView?.ComponentView as CollectionFreeFormView).childDocs) { //how many cusps the scribble hsa - if (cuspArray.length > 4) { - for (let i = 0; i < cuspArray.length - 2; i++) { - let hasDocInTriangle = false; - for (const doc of childDocs) { - const point1 = cuspArray[i]; - const point2 = cuspArray[i + 1]; - const point3 = cuspArray[i + 2]; - const triangleObject = { p1: { X: point1.X, Y: point1.Y }, p2: { X: point2.X, Y: point2.Y }, p3: { X: point3.X, Y: point3.Y } }; - const otherInk = DocumentView.getDocumentView(doc)?.ComponentView as InkingStroke; - if (otherInk instanceof InkingStroke) { - const { inkData: otherInkData } = otherInk?.inkScaledData() ?? { inkData: [] }; - const otherScreenPts = otherInkData.map(point => otherInk.ptToScreen(point)); - if (doc.title === 'line') { - if (this.doesLineIntersectTriangle(otherScreenPts, triangleObject)) { - docsToDelete.push(doc); - hasDocInTriangle = true; - cuspBooleanArray.push(true); - } + for (const doc of childDocs) { + const otherInk = DocumentView.getDocumentView(doc)?.ComponentView as InkingStroke; + if (otherInk instanceof InkingStroke) { + const { inkData: otherInkData } = otherInk?.inkScaledData() ?? { inkData: [] }; + const otherScreenPts = otherInkData.map(point => otherInk.ptToScreen(point)); + if (this.isRectangleOverlap(this.getExtremeCoordinates(otherScreenPts), this.getExtremeCoordinates(inkData))) { + console.log('somethings in'); + const intersects = this.doInksIntersect(inkData, otherScreenPts); + console.log(intersects); + intersects.forEach(intersect => { + let percentage = ''; + if (intersect.includes('/')) { + const leftOfSlash = intersect.split('/')[0]; + percentage = leftOfSlash; } else { - if (this.isAnyPointInTriangle(triangleObject, otherScreenPts)) { - docsToDelete.push(doc); - hasDocInTriangle = true; - cuspBooleanArray.push(true); - } + percentage = intersect; } - } + intersectArray[Math.floor((percentage as unknown as number) * cuspArray.length)] = true; + docsToDelete.push(doc); + }); } - cuspBooleanArray.push(hasDocInTriangle); - } - if (this.determineIfScribble(cuspBooleanArray)) { - docsToDelete.forEach(doc => { - ffView?.ComponentView?.removeDocument?.(doc); - }); - this._points = []; - return true; } } + console.log(intersectArray); + if (intersectArray.length > 3 && this.determineIfScribble(intersectArray)) { + console.log('is a scribble'); + docsToDelete.forEach(doc => { + ffView?.ComponentView?.removeDocument?.(doc); + }); + this._points = []; + return true; + } } return false; } @@ -198,7 +198,7 @@ export class GestureOverlay extends ObservableReactComponent value).length; const percentageTrues = trueCount / cuspBooleanArray.length; - return percentageTrues > 0.65 || hasObjectInFirstAndLast25; + return percentageTrues > 0.5 || hasObjectInFirstAndLast25; } /** * determines if two rectangles are overlapping each other @@ -221,38 +221,6 @@ export class GestureOverlay extends ObservableReactComponent Math.abs((v1.X * (v2.Y - v3.Y) + v2.X * (v3.Y - v1.Y) + v3.X * (v1.Y - v2.Y)) / 2.0); - - const A = area(triangle.p1, triangle.p2, triangle.p3); - - const A1 = area(pt, triangle.p2, triangle.p3); - const A2 = area(triangle.p1, pt, triangle.p3); - const A3 = area(triangle.p1, triangle.p2, pt); - - return A === A1 + A2 + A3; - } - /** - * determines if any points in an array are in a triangle - * @param triangle the triangle with 3 points - * @param points the point in question - * @returns true or false if point is in triangle - */ - // eslint-disable-next-line @typescript-eslint/no-explicit-any - isAnyPointInTriangle(triangle: any, points: any[]): boolean { - for (const point of points) { - if (this.isPointInTriangle(point, triangle)) { - return true; - } - } - return false; - } /** * determines if a line intersects a triangle. used for scribble gesture since the line doesnt have a lot * of points across is so isPointInTriangle will not work for it. @@ -261,20 +229,25 @@ export class GestureOverlay extends ObservableReactComponent view.ComponentView instanceof CollectionFreeFormView); + if (ffView && ffView.ComponentView instanceof CollectionFreeFormView) { + for (let i = 0; i < scribble.length - 3; i += 4) { + // iterate over each segment of bezier curve + for (let j = 0; j < inkStroke.length - 3; j += 4) { + const intersectCurve: Bezier = InkField.Segment(scribble, i); // other curve + const eraserCurve: Bezier = InkField.Segment(inkStroke, j); // eraser curve + if (ffView && ffView.ComponentView instanceof CollectionFreeFormView) { + const result = (ffView.ComponentView as CollectionFreeFormView).bintersects(intersectCurve, eraserCurve)[0]; + if (result !== undefined) { + intersectArray.push(result.toString()); + } + } + } } } - - return false; + return intersectArray; } /** * used in doesLineIntersectTriangle, splits up the triangle into 3 lines and runs this method @@ -284,20 +257,21 @@ export class GestureOverlay extends ObservableReactComponent= 0 && r <= 1 && s >= 0 && s <= 1; + doLinesIntersect(points: any, line: any): boolean { + const ffView = DocumentView.allViews().find(view => view.ComponentView instanceof CollectionFreeFormView); + if (ffView && ffView.ComponentView instanceof CollectionFreeFormView) { + for (let i = 0; i < points.length - 3; i += 4) { + // iterate over each segment of bezier curve + for (let j = 0; j < line.length - 3; j += 4) { + const intersectCurve: Bezier = InkField.Segment(points, i); // other curve + const eraserCurve: Bezier = InkField.Segment(line, j); // eraser curve + if (ffView && ffView.ComponentView instanceof CollectionFreeFormView) { + //(ffView.ComponentView as CollectionFreeFormView).bintersects(); + } + } + } + } + return false; } dryInk = () => { @@ -343,18 +317,31 @@ export class GestureOverlay extends ObservableReactComponent { + const ffView = DocumentView.allViews().find(view => view.ComponentView instanceof CollectionFreeFormView); + const scribbleInk = (ffView?.ComponentView as CollectionFreeFormView).childDocs[(ffView?.ComponentView as CollectionFreeFormView).childDocs.length - 1]; + //this.isScribble((DocumentView.getDocumentView(scribbleInk)?.ComponentView as InkingStroke).inkScaledData().inkData); + }, 1); return this.dispatchGesture(name); case Gestures.RightAngle: return this.convertToText().length > 0; default: - case Gestures.Stroke: - return this.isScribble(); } })(Score < 0.7 ? Gestures.Stroke : (Name as Gestures)); // if no gesture (or if the gesture was unsuccessful), "dry" the stroke into an ink document - if (!actionPerformed) this.dryInk(); + if (!actionPerformed) { + this.dryInk(); + setTimeout(() => { + const ffView = DocumentView.allViews().find(view => view.ComponentView instanceof CollectionFreeFormView); + const scribbleInk = (ffView?.ComponentView as CollectionFreeFormView).childDocs[(ffView?.ComponentView as CollectionFreeFormView).childDocs.length - 1]; + if (this.isScribble((DocumentView.getDocumentView(scribbleInk)?.ComponentView as InkingStroke).inkScaledData().inkData)) { + ffView?.ComponentView?.removeDocument?.(scribbleInk); + } + }, 1); + } } this._points.length = 0; + //console.log((DocumentView.getDocumentView(scribbleInk)?.ComponentView as InkingStroke).inkScaledData()); }; /** * used in the rightAngle gesture to convert handwriting into text. will only work on collections @@ -374,7 +361,7 @@ export class GestureOverlay extends ObservableReactComponent ({ X: p.X, Y: p.Y })); + getCusps(points: InkData) { const arrayOfPoints: { X: number; Y: number }[] = []; arrayOfPoints.push(points[0]); for (let i = 0; i < points.length - 2; i++) { @@ -424,8 +410,8 @@ export class GestureOverlay extends ObservableReactComponent