diff options
Diffstat (limited to 'src/client/views/collections')
| -rw-r--r-- | src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx | 91 |
1 files changed, 53 insertions, 38 deletions
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 78dae87c3..9df9c5492 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -620,6 +620,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection case InkTool.SegmentEraser: case InkTool.RadiusEraser: this._batch = UndoManager.StartBatch('collectionErase'); + this._eraserPts.length = 0; setupMoveUpEvents(this, e, this.onEraserMove, this.onEraserUp, emptyFunction); break; case InkTool.None: @@ -728,6 +729,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection }; _eraserLock = 0; + _eraserPts: number[][] = []; // keep track of the last few eraserPts to make the eraser circle 'stretch' /** * Erases strokes by intersecting them with an invisible "eraser stroke". * By default this iterates through all intersected ink strokes, determines their segmentation, draws back the non-intersected segments, @@ -737,6 +739,8 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection @action onEraserMove = (e: PointerEvent, down: number[], delta: number[]) => { const currPoint = { X: e.clientX, Y: e.clientY }; + this._eraserPts.push([currPoint.X, currPoint.Y]); + this._eraserPts = this._eraserPts.slice(Math.max(0, this._eraserPts.length - 5)); if (this._eraserLock) return false; // bcz: should be fixed by putting it on a queue to be processed after the last eraser movement is processed. this.getEraserIntersections({ X: currPoint.X - delta[0], Y: currPoint.Y - delta[1] }, currPoint).forEach(intersect => { if (!this._deleteList.includes(intersect.inkView)) { @@ -749,7 +753,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection const segments = Doc.ActiveTool === InkTool.SegmentEraser ? this.segmentErase(intersect.inkView, intersect.t) // intersect.t is where the eraser intersected the ink stroke - want to remove the segment that starts at the intersection just before this t value and goes to the one just after it - : this.radiusErase(intersect.inkView, { X: down[0], Y: down[1] }, currPoint); + : this.radiusErase(intersect.inkView, { X: this._eraserPts[0][0], Y: this._eraserPts[0][1] }, currPoint); segments?.forEach(segment => this.forceStrokeGesture( e, @@ -832,16 +836,16 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection const segments: Segment[] = []; const startInkCoords = ink.ComponentView?.ptFromScreen?.(startPt); const inkCoords = ink.ComponentView?.ptFromScreen?.(screenEraserPt); // coordinates in ink space - if (!inkCoords) return []; + if (!inkCoords || !startInkCoords) return []; var eraseSegment: Segment = []; // for eraser visualization const inkStroke = ink?.ComponentView as InkingStroke; const eraserStroke: InkData = inkStroke.splitByEraser(startInkCoords, inkCoords).inkData; const strokeToTVals: Map<InkingStroke, number[]> = new Map<InkingStroke, number[]>(); - for (var i = 0; i < eraserStroke.length - 3; i +=4) { + for (var i = 0; i < eraserStroke.length - 3; i += 4) { eraseSegment.push(InkField.Segment(eraserStroke, i)); // for eraser visualization - this.getOtherInkIntersections(i, eraserStroke, strokeToTVals); + this.getOtherInkIntersections(ink, i, eraserStroke, strokeToTVals); } strokeToTVals.forEach((tVals, inkStroke) => { var segment1: Segment = []; @@ -852,7 +856,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection var hasSplit = false; var continueErasing = false; - + // below is curve splitting logic if (tVals.length) { for (var i = 0; i < inkData.length - 3; i += 4) { @@ -881,12 +885,19 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection } } else if (tVals.length === 1) { if (tVals[0] > currCurveT && tVals[0] < currCurveT + 1) { - if (tVals[0] < Math.floor(inkData.length / 4) - tVals[0]) { + // this heuristic for determine which segment to keep is not quite right even though it will work most of the time. + // We should really store the eraser intersection normal in getOtherInkIntersections, + // and then test its dot product with the tangent of the stroke at the intersection point. + // if the dot product is positive, then erase the first part of the stroke, otherwise the second. + const leftDist = Utils.ptDistance({ x: inkCoords.X, y: inkCoords.Y }, inkSegment.points.lastElement()); + const rightDist = Utils.ptDistance({ x: inkCoords.X, y: inkCoords.Y }, inkSegment.points[0]); + const splits = inkSegment.split(tVals[0] - currCurveT); + if (leftDist < rightDist) { // if it's on the first end - segment1.push(inkSegment.split(tVals[0] - currCurveT, 1)); + segment1.push(splits.left); hasSplit = true; } else { - segment1.push(inkSegment.split(0, tVals[0] - currCurveT)); + segment1.push(splits.right); hasSplit = true; } } else { @@ -1084,6 +1095,15 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection // for some reason bezier.js doesn't handle the case of intersecting a linear curve, so we wrap the intersection // call in a test for linearity bintersects = (curve: Bezier, otherCurve: Bezier) => { + if ((curve as any)._linear) { + // bezier.js doesn't intersect properly if the curve is actually a line -- so get intersect other curve against this line, then figure out the t coordinates of the intersection on this line + const intersections = otherCurve.lineIntersects({ p1: curve.points[0], p2: curve.points[3] }); + if (intersections.length) { + const intPt = otherCurve.get(intersections[0]); + const intT = curve.project(intPt).t; + return intT ? [intT] : []; + } + } if ((otherCurve as any)._linear) { return curve.lineIntersects({ p1: otherCurve.points[0], p2: otherCurve.points[3] }); } @@ -1135,43 +1155,38 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection }; /** - * For radius erase, a function to find an eraser's intersections with all ink strokes on the freeform canvas. + * For radius erase, a function to find an eraser's intersections with a single ink stroke + * @param otherInkDocVIew the ink document that is being erased * @param i the index of the eraser's segment * @param points the eraser's ink data points * @param strokeToTVals Map of InkingStroke to the tVals of its intersections with the eraser - * @returns + * @returns */ - getOtherInkIntersections = (i: number, points: InkData, strokeToTVals: Map<InkingStroke, number[]>): Map<InkingStroke, number[]> => { - // const tVals: [InkingStroke, number[]] = []; - // Iterating through all ink strokes in the current freeform collection. - this.childDocs - .filter(doc => doc.type === DocumentType.INK && !doc.dontIntersect) - .forEach(doc => { - // InkingStroke of other ink strokes - const otherInk = DocumentManager.Instance.getDocumentView(doc, this.DocumentView?.())?.ComponentView as InkingStroke; - // ink Data of other ink strokes - const { inkData: otherInkData } = otherInk?.inkScaledData() ?? { inkData: [] }; - for (var j = 0; j < otherInkData.length - 3; j += 4) { - const curve: Bezier = InkField.Segment(points, i); // eraser curve - const otherCurve: Bezier = InkField.Segment(otherInkData, j); // other curve - this.bintersects(otherCurve, curve).forEach((val: string | number, k: number) => { - // Converting the Bezier.js Split type to a t-value number. - const t = +val.toString().split('/')[0]; - if (k % 2 === 0) { - // here, add to the map - const inkList = strokeToTVals.get(otherInk); - if (inkList !== undefined) { - const inList = inkList.some(val => Math.abs(val - (t + Math.floor(j / 4))) <= 0.01); - if (!inList) { - inkList.push(t + Math.floor(j/4)); - } - } else { - strokeToTVals.set(otherInk, [t + Math.floor(j/4)]); - } + getOtherInkIntersections = (otherInkDocView: DocumentView, i: number, points: InkData, strokeToTVals: Map<InkingStroke, number[]>): Map<InkingStroke, number[]> => { + // InkingStroke of other ink strokes + const otherInk = otherInkDocView.ComponentView as InkingStroke; + // ink Data of other ink strokes + const { inkData: otherInkData } = otherInk?.inkScaledData() ?? { inkData: [] }; + for (var j = 0; j < otherInkData.length - 3; j += 4) { + const curve: Bezier = InkField.Segment(points, i); // eraser curve + const otherCurve: Bezier = InkField.Segment(otherInkData, j); // other curve + this.bintersects(otherCurve, curve).forEach((val: string | number, k: number) => { + // Converting the Bezier.js Split type to a t-value number. + const t = +val.toString().split('/')[0]; + if (k % 2 === 0) { + // here, add to the map + const inkList = strokeToTVals.get(otherInk); + if (inkList !== undefined) { + const inList = inkList.some(val => Math.abs(val - (t + Math.floor(j / 4))) <= 0.01); + if (!inList) { + inkList.push(t + Math.floor(j / 4)); } - }); + } else { + strokeToTVals.set(otherInk, [t + Math.floor(j / 4)]); + } } }); + } return strokeToTVals; }; |
