From cc5cdf6882e25826ae00220927d31201a3bb997e Mon Sep 17 00:00:00 2001
From: vkalev <victor_kalev@brown.edu>
Date: Sun, 21 Nov 2021 19:37:51 -0500
Subject: splitting/deletion occurs but too many t-values are stored

---
 .../collectionFreeForm/CollectionFreeFormView.tsx  | 136 +++++++++++++++++----
 1 file changed, 111 insertions(+), 25 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 6f79c5eab..ce4acb7ad 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -5,7 +5,7 @@ import { DateField } from "../../../../fields/DateField";
 import { Doc, HeightSym, Opt, StrListCast, WidthSym } from "../../../../fields/Doc";
 import { collectionSchema, documentSchema } from "../../../../fields/documentSchemas";
 import { Id } from "../../../../fields/FieldSymbols";
-import { InkData, InkField, InkTool, PointData } from "../../../../fields/InkField";
+import { InkData, InkField, InkTool, PointData, Intersection } from "../../../../fields/InkField";
 import { List } from "../../../../fields/List";
 import { ObjectField } from "../../../../fields/ObjectField";
 import { RichTextField } from "../../../../fields/RichTextField";
@@ -52,6 +52,7 @@ import { MarqueeView } from "./MarqueeView";
 import React = require("react");
 import { ColorScheme } from "../../../util/SettingsManager";
 import { Bezier } from "bezier-js";
+import { GestureOverlay } from "../../GestureOverlay";
 
 export const panZoomSchema = createSchema({
     _panX: "number",
@@ -647,19 +648,8 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
         if (InteractionUtils.IsType(e, InteractionUtils.PENTYPE)) return;
         if (CurrentUserUtils.SelectedTool !== InkTool.None) {
             this._currPoint = { X: e.clientX, Y: e.clientY };
-            const intersections: { [id: number]: DocumentView[] } = this.getEraserIntersections();
-            if (intersections) {
-                for (const t in intersections) {
-                    const inks = intersections[t];
-                    inks.forEach(ink => {
-                        if (!this._deleteList.includes(ink)) {
-                            this._deleteList.push(ink);
-                            // Lowering ink opacity to give the user a visual indicator of deletion.
-                            ink.Document.opacity = 0.5;
-                        }
-                    });
-                }
-            }
+            const eraserIntersections: Intersection[] = this.getEraserIntersections();
+            if (eraserIntersections) this.eraseInkStrokes(eraserIntersections);
         }
         if (InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE)) {
             if (this.props.isContentActive(true)) e.stopPropagation();
@@ -676,12 +666,79 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
         }
     }
 
+    @action
+    eraseInkStrokes = (eraserIntersections: Intersection[]) => {
+        eraserIntersections.forEach(intersect => {
+            var t1, t2;
+            var distT1: number, distT2: number;
+            distT1 = distT2 = Number.MAX_SAFE_INTEGER;
+            const t = intersect.t;
+            const ink = intersect.ink;
+            const ctrlPoints = Cast(ink?.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 curve = new Bezier(array.map(p => ({ x: p.X, y: p.Y })));
+                const inkIntersections: number[] = this.getInkIntersections(curve);
+                inkIntersections.forEach(currentT => {
+                    const diff = t - currentT;
+                    if (diff > 0 && diff < distT1) {
+                        distT1 = diff;
+                        t1 = currentT;
+                    } else if (diff < 0 && diff < distT2) {
+                        distT2 = -diff;
+                        t2 = currentT;
+                    }
+                });
+
+                // Normal case --> deletion of entire stroke
+                if (!t1 && !t2) {
+                    if (!this._deleteList.includes(ink)) {
+                        this._deleteList.push(ink);
+                        // Lowering ink opacity to give the user a visual indicator of deletion.
+                        ink.Document.opacity = 0.5;
+                    }
+                } else if (t1 && !t2) {
+                    const splitCurve = curve.split(t1);
+                    const leftInkData: PointData[] = splitCurve.left.points.map(p => ({ X: p.x, Y: p.y }));
+                    GestureOverlay.Instance.dispatchGesture(GestureUtils.Gestures.Stroke, leftInkData);
+                    if (!this._deleteList.includes(ink)) {
+                        this._deleteList.push(ink);
+                        // Lowering ink opacity to give the user a visual indicator of deletion.
+                        ink.Document.opacity = 0.5;
+                    }
+                } else if (!t1 && t2) {
+                    const splitCurve = curve.split(t2);
+                    const rightInkData: PointData[] = splitCurve.right.points.map(p => ({ X: p.x, Y: p.y }));
+                    GestureOverlay.Instance.dispatchGesture(GestureUtils.Gestures.Stroke, rightInkData);
+                    if (!this._deleteList.includes(ink)) {
+                        this._deleteList.push(ink);
+                        // Lowering ink opacity to give the user a visual indicator of deletion.
+                        ink.Document.opacity = 0.5;
+                    }
+                } else if (t1 && t2) {
+                    const splitCurve1 = curve.split(t1);
+                    const leftInkData: PointData[] = splitCurve1.left.points.map(p => ({ X: p.x, Y: p.y }));
+                    GestureOverlay.Instance.dispatchGesture(GestureUtils.Gestures.Stroke, leftInkData);
+                    const splitCurve2 = curve.split(t2);
+                    const rightInkData: PointData[] = splitCurve2.right.points.map(p => ({ X: p.x, Y: p.y }));
+                    GestureOverlay.Instance.dispatchGesture(GestureUtils.Gestures.Stroke, rightInkData);
+
+                    if (!this._deleteList.includes(ink)) {
+                        this._deleteList.push(ink);
+                        // Lowering ink opacity to give the user a visual indicator of deletion.
+                        ink.Document.opacity = 0.5;
+                    }
+                }
+            }
+        });
+    }
+
     /**
      * Determines if the Eraser tool has intersected with an ink stroke in the current freeform collection.
-     * @returns A dictionary mapping the t-value intersection of the eraser with a list of ink DocumentViews.
+     * @returns A dictionary mapping the t-value intersection of the eraser with the corresponding ink DocumentView.
      */
-    getEraserIntersections = (): { [id: number]: DocumentView[] } => {
-        const intersections: { [id: number]: DocumentView[] } = {};
+    getEraserIntersections = (): Intersection[] => {
+        const intersections: Intersection[] = [];
         this.childDocs
             .filter(doc => doc.type === DocumentType.INK)
             .forEach(doc => {
@@ -694,14 +751,13 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
                     const currPointInkSpace = inkView?.ComponentView?.ptFromScreen?.(this._currPoint);
                     if (prevPointInkSpace && currPointInkSpace) {
                         const curve = new Bezier(array.map(p => ({ x: p.X, y: p.Y })));
-                        const t = curve.intersects(
-                            {
-                                p1: { x: prevPointInkSpace.X, y: prevPointInkSpace.Y },
-                                p2: { x: currPointInkSpace.X, y: currPointInkSpace.Y }
-                            });
-                        if (inkView && t) {
-                            t.forEach(val => {
-                                intersections[+t[0]] ? intersections[+t[0]].push(inkView) : intersections[+t[0]] = [inkView];
+                        const intersects = curve.intersects({
+                            p1: { x: prevPointInkSpace.X, y: prevPointInkSpace.Y },
+                            p2: { x: currPointInkSpace.X, y: currPointInkSpace.Y }
+                        });
+                        if (inkView && intersects) {
+                            intersects.forEach(t => {
+                                intersections.push({ t: +t.toString(), ink: inkView });
                             });
                         }
                     }
@@ -710,6 +766,36 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
         return intersections;
     }
 
+    getInkIntersections = (curve: Bezier): number[] => {
+        const intersections: number[] = [];
+        const selfIntersect = curve.selfintersects();
+        if (selfIntersect) {
+            selfIntersect.forEach(t => {
+                intersections.push(+t.toString());
+            });
+        }
+
+        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 i = 0; i < currCtrls.length - 3; i += 4) {
+                    const currArray = [currCtrls[i], currCtrls[i + 1], currCtrls[i + 2], currCtrls[i + 3]];
+                    const currCurve = new Bezier(currArray.map(p => ({ x: p.X, y: p.Y })));
+                    const intersect = curve.intersects(currCurve);
+                    if (intersect) {
+                        intersect.forEach(t => {
+                            intersections.push(+this.parseTValue(t.toString()));
+                        });
+                    }
+                }
+            });
+        return intersections;
+    }
+
+    parseTValue = (t: string) => t.split("/", 1)[0];
+
     handle1PointerMove = (e: TouchEvent, me: InteractionUtils.MultiTouchEvent<TouchEvent>) => {
         if (!e.cancelBubble) {
             const myTouches = InteractionUtils.GetMyTargetTouches(me, this.prevPoints, true);
-- 
cgit v1.2.3-70-g09d2

