From 8405ed2e3ebaf7ba7842a5619e9b252bf5eb9c4c Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Thu, 25 Nov 2021 21:45:06 -0500
Subject: fixed up erasing ink by segments

---
 .../collectionFreeForm/CollectionFreeFormView.tsx  | 156 +++++++++------------
 1 file changed, 65 insertions(+), 91 deletions(-)

(limited to 'src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx')

diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 4aac49469..df690da49 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -34,7 +34,7 @@ import { COLLECTION_BORDER_WIDTH } from "../../../views/global/globalCssVariable
 import { Timeline } from "../../animationtimeline/Timeline";
 import { ContextMenu } from "../../ContextMenu";
 import { DocumentDecorations } from "../../DocumentDecorations";
-import { ActiveArrowEnd, ActiveArrowStart, ActiveDash, ActiveFillColor, ActiveInkBezierApprox, ActiveInkColor, ActiveInkWidth } from "../../InkingStroke";
+import { ActiveArrowEnd, ActiveArrowStart, ActiveDash, ActiveFillColor, ActiveInkBezierApprox, ActiveInkColor, ActiveInkWidth, InkingStroke, SetActiveInkWidth, SetActiveFillColor, SetActiveInkColor } from "../../InkingStroke";
 import { LightboxView } from "../../LightboxView";
 import { CollectionFreeFormDocumentView } from "../../nodes/CollectionFreeFormDocumentView";
 import { DocFocusOptions, DocumentView, DocumentViewProps, ViewAdjustment, ViewSpecPrefix } from "../../nodes/DocumentView";
@@ -610,11 +610,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
             this.removeMoveListeners();
             this.removeEndListeners();
             if (CurrentUserUtils.SelectedTool !== InkTool.None) {
-                if (this._deleteList.length > 0) {
-                    this._deleteList.forEach(ink => {
-                        ink.props.removeDocument?.(ink.props.Document);
-                    });
-                }
+                this._deleteList.forEach(ink => ink.props.removeDocument?.(ink.rootDoc));
                 this._prevPoint = this._currPoint = { X: -1, Y: -1 };
                 this._deleteList = [];
                 this._batch?.end();
@@ -650,8 +646,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
         if (CurrentUserUtils.SelectedTool !== InkTool.None) {
             this._currPoint = { X: e.clientX, Y: e.clientY };
             // Erasing ink strokes if intersections occur.
-            const eraserIntersections: Intersection[] = this.getEraserIntersections();
-            if (eraserIntersections) this.eraseInkStrokes(eraserIntersections);
+            this.eraseInkStrokes(this.getEraserIntersections());
         }
         if (InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE)) {
             if (this.props.isContentActive(true)) e.stopPropagation();
@@ -676,21 +671,18 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
     eraseInkStrokes = (eraserIntersections: Intersection[]) => {
         eraserIntersections.forEach(intersect => {
             const ink = intersect.ink;
-            if (ink) {
-                if (!this._deleteList.includes(ink)) {
-                    const segments: Segment[] = this.segmentInkStroke(ink, intersect.index ?? 0);
-                    segments.forEach(segment => {
-                        const newInkData: PointData[] = [];
-                        // Appending all curves of the current segment together in order to render a single new stroke.
-                        segment.forEach(curve => {
-                            newInkData.push(...curve.points.map(p => ({ X: p.x, Y: p.y })));
-                        });
-                        GestureOverlay.Instance.dispatchGesture(GestureUtils.Gestures.Stroke, newInkData);
-                    });
-                    this._deleteList.push(ink);
-                    // Lowering ink opacity to give the user a visual indicator of deletion.
-                    ink.Document.opacity = 0.5;
-                }
+            if (ink && !this._deleteList.includes(ink)) {
+                this._deleteList.push(ink);
+                SetActiveInkWidth(StrCast(ink.rootDoc.strokeWidth?.toString()) || "1");
+                SetActiveInkColor(StrCast(ink.rootDoc.color?.toString()) || "black");
+                // create a new curve by appending all curves of the current segment together in order to render a single new stroke.
+                this.segmentInkStroke(ink, intersect.t ?? 0).forEach(segment =>
+                    GestureOverlay.Instance.dispatchGesture(GestureUtils.Gestures.Stroke,
+                        segment.reduce((data, curve) => [...data, ...curve.points
+                            .map(p => ink.ComponentView?.ptToScreen?.({ X: p.x, Y: p.y }) ?? { X: 0, Y: 0 })
+                        ], [] as PointData[])));
+                // Lower ink opacity to give the user a visual indicator of deletion.
+                ink.layoutDoc.opacity = 0.5;
             }
         });
     }
@@ -705,12 +697,13 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
             .filter(doc => doc.type === DocumentType.INK)
             .forEach(doc => {
                 const inkView = DocumentManager.Instance.getDocumentView(doc, this.props.CollectionView);
-                const ctrlPoints = Cast(inkView?.dataDoc[this.props.fieldKey], InkField)?.inkData ?? [];
-                for (var i = 0; i < ctrlPoints.length - 3; i += 4) {
-                    const array = [ctrlPoints[i], ctrlPoints[i + 1], ctrlPoints[i + 2], ctrlPoints[i + 3]];
+                const inkStroke = inkView?.ComponentView as InkingStroke;
+                const { inkData } = inkStroke?.inkScaledData();
+                for (var i = 0; i < inkData.length - 3; i += 4) {
+                    const array = inkData.slice(i, i + 4);
                     // Converting from screen space to ink space for the intersection.
-                    const prevPointInkSpace = inkView?.ComponentView?.ptFromScreen?.(this._prevPoint);
-                    const currPointInkSpace = inkView?.ComponentView?.ptFromScreen?.(this._currPoint);
+                    const prevPointInkSpace = inkStroke?.ptFromScreen?.(this._prevPoint);
+                    const currPointInkSpace = inkStroke?.ptFromScreen?.(this._currPoint);
                     if (prevPointInkSpace && currPointInkSpace) {
                         const curve = new Bezier(array.map(p => ({ x: p.X, y: p.Y })));
                         const intersects = curve.intersects({
@@ -720,7 +713,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
                         if (inkView && intersects) {
                             for (const val of intersects) {
                                 // Casting t-value from type: (string | number) to number for comparisons.
-                                const t = +val.toString();
+                                const t = +(Number(val) + Math.floor(i / 4)).toString(); // add start of curve segment to convert from local t value to t value along complete curve
                                 var unique: boolean = true;
                                 // Ensuring there are no duplicate intersections in the list returned.
                                 for (const prevIntersect of intersections) {
@@ -729,7 +722,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
                                         break;
                                     }
                                 }
-                                if (unique) intersections.push({ t: +t.toString(), ink: inkView, curve: curve, index: i });
+                                if (unique) intersections.push({ t: +t.toString(), ink: inkView, curve: curve });
                             }
                         }
                     }
@@ -742,57 +735,40 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
      * Performs segmentation of the ink stroke - creates "segments" or subsections of the current ink stroke at points in which the
      * ink stroke intersects any other ink stroke (including itself).
      * @param ink The ink DocumentView intersected by the eraser.
-     * @param excludeCurve The index of the curve in the ink document that the eraser intersection occurred.
+     * @param excludeT The index of the curve in the ink document that the eraser intersection occurred.
      * @returns The ink stroke represented as a list of segments, excluding the segment in which the eraser intersection occurred.
      */
     @action
-    segmentInkStroke = (ink: DocumentView, excludeCurve: number): Segment[] => {
+    segmentInkStroke = (ink: DocumentView, excludeT: number): Segment[] => {
         const segments: Segment[] = [];
         var segment: Segment = [];
-        var excludeSegment: boolean = false;
-        const ctrlPoints = Cast(ink?.dataDoc[this.props.fieldKey], InkField)?.inkData ?? [];
-        // Iterating through all of the curves of the intersected ink stroke.
-        for (var i = 0; i < ctrlPoints.length - 3; i += 4) {
-            // Performing "deletion" of a segment by excluding the segment in which intersection with the eraser occurred.
-            if (i === excludeCurve) excludeSegment = true;
-            const array = [ctrlPoints[i], ctrlPoints[i + 1], ctrlPoints[i + 2], ctrlPoints[i + 3]];
-            const curve = new Bezier(array.map(p => ({ x: p.X, y: p.Y })));
+        var startSegmentT = 0;
+        const { inkData } = (ink?.ComponentView as InkingStroke).inkScaledData();
+        // This iterates through all segments of the curve and splits them where they intersect another curve.
+        // if 'excludeT' is specified, then any segment containing excludeT will be skipped (ie, deleted)
+        for (var i = 0; i < inkData.length - 3; i += 4) {
+            const curve = new Bezier(inkData.slice(i, i + 4).map(p => ({ x: p.X, y: p.Y })));
             // Getting all t-value intersections of the current curve with all other curves.
-            const tVals: number[] = this.getInkIntersections(i, ink, curve);
-            if (tVals.length > 0) {
-                tVals.sort();
-                const curveCopy = new Bezier(curve.points);
-                // Splitting only by the first t-value.
-                const pair = curveCopy.split(tVals[0]);
-                segment.push(pair.left);
-                if (!excludeSegment) {
-                    segments.push(segment);
-                    segment = [pair.right];
-                    excludeSegment = false;
-                }
-                // Splitting by the number of t-values returned.
-                // for (var index = 0; index < tVals.length; index++) {
-                //     const curveCopy = new Bezier(curve.points);
-                //     if (index === 0) {
-                //         const pair = curveCopy.split(tVals[index]);
-                //         segment.push(pair.left);
-                //         segments.push(segment);
-                //         segment = [pair.right];
-                //     } else if (index === tVals.length - 1) {
-                //         segments.push(segment);
-                //         const pair = curveCopy.split(tVals[index]);
-                //         segment = [pair.right];
-                //     } else {
-                //         segments.push(segment);
-                //         const curve = curveCopy.split(index, index + 1);
-                //         segment = [curve];
-                //     }
-                // }
+            const tVals = this.getInkIntersections(i, ink, curve).sort();
+            if (tVals.length) {
+                tVals.forEach((t, index) => {
+                    const docCurveTVal = t + Math.floor(i / 4);
+                    if (excludeT < startSegmentT || excludeT > docCurveTVal) {
+                        const localStartTVal = startSegmentT - Math.floor(i / 4);
+                        segment.push(curve.split(localStartTVal < 0 ? 0 : localStartTVal, t));
+                        segment.length && segments.push(segment);
+                    }
+                    // start a new segment from the intersection t value
+                    segment = tVals.length - 1 === index ? [curve.split(t).right] : [];
+                    startSegmentT = docCurveTVal;
+                });
             } else {
                 segment.push(curve);
             }
         }
-        if (!excludeSegment) segments.push(segment);
+        if (excludeT < startSegmentT || excludeT > (inkData.length / 4)) {
+            segment.length && segments.push(segment);
+        }
         return segments;
     }
 
@@ -810,23 +786,21 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
         this.childDocs
             .filter(doc => doc.type === DocumentType.INK)
             .forEach(doc => {
-                const currInk = DocumentManager.Instance.getDocumentView(doc, this.props.CollectionView);
-                const currCtrls = Cast(currInk?.dataDoc[this.props.fieldKey], InkField)?.inkData ?? [];
-                for (var j = 0; j < currCtrls.length - 3; j += 4) {
-                    const exclude = i === j || i === j - 4 || i === j + 4;
-                    const sameDoc = ink?.Document === currInk?.Document;
+                const otherInk = DocumentManager.Instance.getDocumentView(doc, this.props.CollectionView)?.ComponentView as InkingStroke;
+                const { inkData: otherInkData } = otherInk.inkScaledData();
+                const otherScreenPts = otherInkData.map(point => otherInk.ptToScreen(point));
+                const otherCtrlPts = otherScreenPts.map(spt => (ink.ComponentView as InkingStroke).ptFromScreen(spt));
+                for (var j = 0; j < otherCtrlPts.length - 3; j += 4) {
+                    const neighboringSegment = i === j || i === j - 4 || i === j + 4;
                     // Ensuring that the curve intersected by the eraser is not checked for further ink intersections.
-                    if (sameDoc && exclude) continue;
-                    const currArray = [currCtrls[j], currCtrls[j + 1], currCtrls[j + 2], currCtrls[j + 3]];
-                    const currCurve = new Bezier(currArray.map(p => ({ x: p.X, y: p.Y })));
-                    const intersect = curve.intersects(currCurve);
-                    if (intersect.length > 0) {
-                        intersect.forEach(val => {
-                            // Converting the Bezier.js Split type to a t-value number.
-                            const t = +val.toString().split("/")[0];
-                            if (!tVals.includes(t)) tVals.push(t);
-                        });
-                    }
+                    if (ink?.Document === otherInk.props.Document && neighboringSegment) continue;
+
+                    const otherCurve = new Bezier(otherCtrlPts.slice(j, j + 4).map(p => ({ x: p.X, y: p.Y })));
+                    curve.intersects(otherCurve).forEach((val: string | number, i: number) => {
+                        // Converting the Bezier.js Split type to a t-value number.
+                        const t = +val.toString().split("/")[0];
+                        if (i % 2 === 0 && !tVals.includes(t)) tVals.push(t); // bcz: Hack!  don't know why but intersection points are doubled from bezier.js (but not identical).  
+                    });
                 }
             });
         return tVals;
@@ -1006,10 +980,10 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
                 .filter(({ pos, size }) => pos && size).map(({ pos, size }) => ({ pos: pos!, size: size! }));
             if (measuredDocs.length) {
                 const ranges = measuredDocs.reduce(({ xrange, yrange }, { pos, size }) =>  // computes range of content
-                ({
-                    xrange: { min: Math.min(xrange.min, pos.x), max: Math.max(xrange.max, pos.x + (size.width || 0)) },
-                    yrange: { min: Math.min(yrange.min, pos.y), max: Math.max(yrange.max, pos.y + (size.height || 0)) }
-                })
+                    ({
+                        xrange: { min: Math.min(xrange.min, pos.x), max: Math.max(xrange.max, pos.x + (size.width || 0)) },
+                        yrange: { min: Math.min(yrange.min, pos.y), max: Math.max(yrange.max, pos.y + (size.height || 0)) }
+                    })
                     , {
                         xrange: { min: Number.MAX_VALUE, max: -Number.MAX_VALUE },
                         yrange: { min: Number.MAX_VALUE, max: -Number.MAX_VALUE }
-- 
cgit v1.2.3-70-g09d2

