From 0c33bc8033c9877abbe6e4074a687559bc4948d0 Mon Sep 17 00:00:00 2001
From: eleanor-park <eleanor_park@brown.edu>
Date: Tue, 23 Apr 2024 15:16:06 -0400
Subject: erase multiple segments bug

---
 .../collectionFreeForm/CollectionFreeFormView.tsx  | 167 ++++++++++++++-------
 1 file changed, 114 insertions(+), 53 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 60f7662ff..78dae87c3 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -749,10 +749,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, intersect.t, currPoint);
-                    // } else if (Doc.ActiveTool === InkTool.RadiusEraser) {
-                    //     segments = undefined;
-                    // }
+                            : this.radiusErase(intersect.inkView, { X: down[0], Y: down[1] }, currPoint);
                     segments?.forEach(segment =>
                         this.forceStrokeGesture(
                             e,
@@ -763,7 +760,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
                     setTimeout(() => this._eraserLock--);
                 }
                 // Lower ink opacity to give the user a visual indicator of deletion.
-                intersect.inkView.layoutDoc.opacity = 0.0;
+                intersect.inkView.layoutDoc.opacity = 0.2;
                 intersect.inkView.layoutDoc.dontIntersect = true;
             }
         });
@@ -831,63 +828,86 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
     };
 
     @action
-    radiusErase = (ink: DocumentView, eraseT: number, screenEraserPt: { X: number; Y: number }): Segment[] => {
+    radiusErase = (ink: DocumentView, startPt: { X: number; Y: number }, screenEraserPt: { X: number; Y: number }): Segment[] => {
         const segments: Segment[] = [];
+        const startInkCoords = ink.ComponentView?.ptFromScreen?.(startPt);
         const inkCoords = ink.ComponentView?.ptFromScreen?.(screenEraserPt); // coordinates in ink space
         if (!inkCoords) return [];
 
-        var segment1: Segment = [];
-        var segment2: Segment = [];
-        const eraseRadius = ActiveInkWidth() / 2;
+        var eraseSegment: Segment = []; // for eraser visualization
         const inkStroke = ink?.ComponentView as InkingStroke;
-        const { inkData } = inkStroke.inkScaledData();
-
-        const eraserInkData = inkStroke.splitByEraser(inkCoords, eraseRadius);
 
-        const tVals: number[] = []; // should be the tvals of the intersections
+        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) {
+            eraseSegment.push(InkField.Segment(eraserStroke, i)); // for eraser visualization
+            this.getOtherInkIntersections(i, eraserStroke, strokeToTVals);
+        }
+        strokeToTVals.forEach((tVals, inkStroke) => {
+            var segment1: Segment = [];
+            var segment2: Segment = [];
+            const { inkData } = inkStroke.inkScaledData();
+            tVals.sort();
+            console.log('TVALS', inkStroke, tVals);
 
-        for (var i = 0; i < eraserInkData.length - 3; i += 4) {
-            const eraserBezier: Bezier = InkField.Segment(eraserInkData, i);
-            segment1.push(eraserBezier);
-            for (var j = 0; j < inkData.length; j += 4) {
-                const inkSegment: Bezier = InkField.Segment(inkData, i);
-                // this.bintersects(inkSegment, eraserBezier).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).
-                // });
+            var hasSplit = false;
+            var continueErasing = false;
+            
+            // below is curve splitting logic
+            if (tVals.length) {
+                for (var i = 0; i < inkData.length - 3; i += 4) {
+                    const inkSegment: Bezier = InkField.Segment(inkData, i);
+                    const currCurveT = Math.floor(i / 4);
+
+                    if (tVals.length === 2) {
+                        if (tVals[0] > currCurveT && tVals[0] < currCurveT + 1) {
+                            segment1.push(inkSegment.split(0, tVals[0] - currCurveT));
+                            continueErasing = true;
+                            if (tVals[1] > currCurveT && tVals[1] < currCurveT + 1) {
+                                segment2.push(inkSegment.split(tVals[1] - currCurveT, 1));
+                                continueErasing = false;
+                                hasSplit = true;
+                            }
+                        } else if (tVals[1] > currCurveT && tVals[1] < currCurveT + 1) {
+                            segment2.push(inkSegment.split(tVals[1] - currCurveT, 1));
+                            continueErasing = false;
+                            hasSplit = true;
+                        } else if (!continueErasing) {
+                            if (hasSplit) {
+                                segment2.push(inkSegment);
+                            } else {
+                                segment1.push(inkSegment);
+                            }
+                        }
+                    } else if (tVals.length === 1) {
+                        if (tVals[0] > currCurveT && tVals[0] < currCurveT + 1) {
+                            if (tVals[0] < Math.floor(inkData.length / 4) - tVals[0]) {
+                                // if it's on the first end
+                                segment1.push(inkSegment.split(tVals[0] - currCurveT, 1));
+                                hasSplit = true;
+                            } else {
+                                segment1.push(inkSegment.split(0, tVals[0] - currCurveT));
+                                hasSplit = true;
+                            }
+                        } else {
+                            if (tVals[0] < Math.floor(inkData.length / 4) - tVals[0] && hasSplit) {
+                                segment1.push(inkSegment);
+                            } else if (tVals[0] >= Math.floor(inkData.length / 4) - tVals[0] && !hasSplit) {
+                                segment1.push(inkSegment);
+                            }
+                        }
+                    }
+                }
             }
-        }
-        // segment1.push(eraserBezier);
-
-        // for (var i = 0; i < inkData.length - 3; i += 4) {
-        //     const currCurveT = Math.floor(i/4);
-        //     const inkSegment: Bezier = InkField.Segment(inkData, i);
-        //     if (tVals[0] >= currCurveT && tVals[0] < currCurveT+1) {
-        //         tVals.shift()
-        //         i -= 4;
-        //         if (eraseT - eraseWidth > currCurveT && eraseT + eraseWidth < currCurveT + 1) {
-        //             segment1.push(inkSegment.split(0, eraseT - currCurveT - eraseWidth));
-        //             segment2.push(inkSegment.split(eraseT - currCurveT + eraseWidth, 1));
-        //         } else if (eraseT - eraseWidth < currCurveT) {
-        //             segment2.push(inkSegment.split(eraseT - currCurveT + eraseWidth, 1));
-        //         } else if (eraseT + eraseWidth > currCurveT + 1) {
-        //             segment1.push(inkSegment.split(0, eraseT - currCurveT - eraseWidth));
-        //         }
-        //     } else if (eraseT > currCurveT + 1) {
-        //         segment1.push(inkSegment);
-        //     } else {
-        //         segment2.push(inkSegment);
-        //     }
-        // }
+            if (segment1.length && (Math.abs(segment1[0].points[0].x - segment1[0].points.lastElement().x) > 0.5 || Math.abs(segment1[0].points[0].y - segment1[0].points.lastElement().y) > 0.5)) {
+                segments.push(segment1);
+            }
+            if (segment2.length && (Math.abs(segment2[0].points[0].x - segment2[0].points.lastElement().x) > 0.5 || Math.abs(segment2[0].points[0].y - segment2[0].points.lastElement().y) > 0.5)) {
+                segments.push(segment2);
+            }
+        });
 
-        // push 1 or both segments if they are not empty
-        if (segment1.length && (Math.abs(segment1[0].points[0].x - segment1[0].points.lastElement().x) > 0.5 || Math.abs(segment1[0].points[0].y - segment1[0].points.lastElement().y) > 0.5)) {
-            segments.push(segment1);
-        }
-        if (segment2.length && (Math.abs(segment2[0].points[0].x - segment2[0].points.lastElement().x) > 0.5 || Math.abs(segment2[0].points[0].y - segment2[0].points.lastElement().y) > 0.5)) {
-            segments.push(segment2);
-        }
+        segments.push(eraseSegment); // for eraser visualization
         return segments;
     };
 
@@ -1114,6 +1134,47 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         return tVals;
     };
 
+    /**
+     * For radius erase, a function to find an eraser's intersections with all ink strokes on the freeform canvas.
+     * @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 
+     */
+    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)]);
+                            }
+                        }
+                    });
+                }
+            });
+        return strokeToTVals;
+    };
+
     @action
     zoom = (pointX: number, pointY: number, deltaY: number): void => {
         if (this.Document.isGroup || this.Document[(this._props.viewField ?? '_') + 'freeform_noZoom']) return;
-- 
cgit v1.2.3-70-g09d2

