From f6775da5d14f35be2d05de13008f86fbae2cbf68 Mon Sep 17 00:00:00 2001
From: Lauren <victor_kalev@brown.edu>
Date: Fri, 29 Oct 2021 13:57:37 -0400
Subject: working on ink stroke intersection

---
 .../collectionFreeForm/CollectionFreeFormView.tsx  | 27 +++++++++++++++++++++-
 1 file changed, 26 insertions(+), 1 deletion(-)

(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 febccbfcc..f0064d15e 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 } from "../../../../fields/InkField";
+import { InkData, InkField, InkTool, PointData } from "../../../../fields/InkField";
 import { List } from "../../../../fields/List";
 import { ObjectField } from "../../../../fields/ObjectField";
 import { RichTextField } from "../../../../fields/RichTextField";
@@ -51,6 +51,7 @@ import "./CollectionFreeFormView.scss";
 import { MarqueeView } from "./MarqueeView";
 import React = require("react");
 import { ColorScheme } from "../../../util/SettingsManager";
+import { Bezier } from "bezier-js";
 
 export const panZoomSchema = createSchema({
     _panX: "number",
@@ -111,6 +112,8 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
     @observable _pullDirection: string = "";
     @observable _showAnimTimeline = false;
     @observable _clusterSets: (Doc[])[] = [];
+    @observable _prevPoint: PointData = { X: -1, Y: -1 };
+    @observable _currPoint: PointData = { X: -1, Y: -1 };
     @observable _timelineRef = React.createRef<Timeline>();
     @observable _marqueeRef = React.createRef<HTMLDivElement>();
     @observable _keyframeEditing = false;
@@ -450,6 +453,11 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
             }
             // eraser plus anything else mode
             else {
+                if (InteractionUtils.IsType(e, InteractionUtils.ERASERTYPE)) {
+                    this._prevPoint = this._currPoint = { X: e.clientX, Y: e.clientY };
+                    const intersectStroke = this.getInkIntersection();
+                    if (intersectStroke) intersectStroke.props.removeDocument?.(intersectStroke.props.Document);
+                }
                 e.stopPropagation();
                 e.preventDefault();
             }
@@ -595,6 +603,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
 
     onPointerUp = (e: PointerEvent): void => {
         if (!InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE)) {
+            this._prevPoint = this._currPoint = { X: -1, Y: -1 };
             document.removeEventListener("pointermove", this.onPointerMove);
             document.removeEventListener("pointerup", this.onPointerUp);
             this.removeMoveListeners();
@@ -627,6 +636,10 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
     onPointerMove = (e: PointerEvent): void => {
         if (this.props.Document._isGroup) return; // groups don't pan when dragged -- instead let the event go through to allow the group itself to drag
         if (InteractionUtils.IsType(e, InteractionUtils.PENTYPE)) return;
+        if (InteractionUtils.IsType(e, InteractionUtils.ERASERTYPE)) {
+            this._prevPoint = this._currPoint;
+            this._currPoint = { X: e.clientX, Y: e.clientY }
+        }
         if (InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE)) {
             if (this.props.isContentActive(true)) e.stopPropagation();
         } else if (!e.cancelBubble) {
@@ -642,6 +655,18 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
         }
     }
 
+    getInkIntersection = (): DocumentView | null => {
+        // this.children.filter((doc: Doc) => doc.type === DocumentType.INK)
+        // .forEach((doc: Doc) => {
+        //     const inkView = DocumentManager.Instance.getDocumentView(doc, this.props.CollectionView);
+        //     const ctrlPoints = inkView?.ComponentView?.inkScaledData().inkData.slice();
+        //     const array = [ctrlPoints[i], ctrlPoints[i + 1], ctrlPoints[i + 2], ctrlPoints[i + 3]];
+        //     const intersects = new Bezier(array.map(p => ({ x: p.X, y: p.Y }))).intersects({p1: {x: this._prevPoint.X, y: this._prevPoint.Y}, p2: {x: this._currPoint.X, y: this._currPoint.Y}});
+        //     if (intersects) return inkView;
+        // });
+        return null;
+    }
+
     handle1PointerMove = (e: TouchEvent, me: InteractionUtils.MultiTouchEvent<TouchEvent>) => {
         if (!e.cancelBubble) {
             const myTouches = InteractionUtils.GetMyTargetTouches(me, this.prevPoints, true);
-- 
cgit v1.2.3-70-g09d2


From 2e6c1f2c1710548ebf86476e1476c22889f11819 Mon Sep 17 00:00:00 2001
From: Lauren <victor_kalev@brown.edu>
Date: Fri, 5 Nov 2021 18:27:07 -0400
Subject: ink document iteration not working

---
 src/client/views/InkingStroke.tsx                  |  2 +-
 .../collectionFreeForm/CollectionFreeFormView.tsx  | 52 +++++++++++++++-------
 2 files changed, 36 insertions(+), 18 deletions(-)

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

diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx
index 81a888256..ecb46a5b3 100644
--- a/src/client/views/InkingStroke.tsx
+++ b/src/client/views/InkingStroke.tsx
@@ -130,7 +130,7 @@ export class InkingStroke extends ViewBoxBaseComponent<FieldViewProps, InkDocume
         const inkPt = {
             X: (docPt[0] - inkStrokeWidth / 2) / inkScaleX + inkStrokeWidth / 2 + inkLeft,
             Y: (docPt[1] - inkStrokeWidth / 2) / inkScaleY + inkStrokeWidth / 2 + inkTop,
-        }
+        };
         return inkPt;
     }
     ptToScreen = (inkPt: { X: number, Y: number }) => {
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index f0064d15e..0941db5f8 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -114,6 +114,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
     @observable _clusterSets: (Doc[])[] = [];
     @observable _prevPoint: PointData = { X: -1, Y: -1 };
     @observable _currPoint: PointData = { X: -1, Y: -1 };
+    @observable _deleteList: DocumentView[] = [];
     @observable _timelineRef = React.createRef<Timeline>();
     @observable _marqueeRef = React.createRef<HTMLDivElement>();
     @observable _keyframeEditing = false;
@@ -453,11 +454,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
             }
             // eraser plus anything else mode
             else {
-                if (InteractionUtils.IsType(e, InteractionUtils.ERASERTYPE)) {
-                    this._prevPoint = this._currPoint = { X: e.clientX, Y: e.clientY };
-                    const intersectStroke = this.getInkIntersection();
-                    if (intersectStroke) intersectStroke.props.removeDocument?.(intersectStroke.props.Document);
-                }
+                this._prevPoint = { X: e.clientX, Y: e.clientY };
                 e.stopPropagation();
                 e.preventDefault();
             }
@@ -603,11 +600,17 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
 
     onPointerUp = (e: PointerEvent): void => {
         if (!InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE)) {
-            this._prevPoint = this._currPoint = { X: -1, Y: -1 };
             document.removeEventListener("pointermove", this.onPointerMove);
             document.removeEventListener("pointerup", this.onPointerUp);
             this.removeMoveListeners();
             this.removeEndListeners();
+            if (CurrentUserUtils.SelectedTool !== InkTool.None) {
+                this._deleteList.forEach(ink => {
+                    ink.props.removeDocument?.(ink.props.Document);
+                });
+                this._prevPoint = this._currPoint = { X: -1, Y: -1 };
+                this._deleteList = [];
+            }
         }
     }
 
@@ -636,9 +639,15 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
     onPointerMove = (e: PointerEvent): void => {
         if (this.props.Document._isGroup) return; // groups don't pan when dragged -- instead let the event go through to allow the group itself to drag
         if (InteractionUtils.IsType(e, InteractionUtils.PENTYPE)) return;
-        if (InteractionUtils.IsType(e, InteractionUtils.ERASERTYPE)) {
-            this._prevPoint = this._currPoint;
-            this._currPoint = { X: e.clientX, Y: e.clientY }
+        if (CurrentUserUtils.SelectedTool !== InkTool.None) {
+            this._currPoint = { X: e.clientX, Y: e.clientY };
+            const intersectStroke = this.getInkIntersection();
+            if (intersectStroke) {
+                if (!this._deleteList.includes(intersectStroke)) {
+                    this._deleteList.push(intersectStroke);
+                    // lower intersectStroke opacity to give user a visual indicator
+                }
+            }
         }
         if (InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE)) {
             if (this.props.isContentActive(true)) e.stopPropagation();
@@ -655,15 +664,24 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
         }
     }
 
+    /**
+     * Determines if the Eraser tool has intersected with an ink stroke in the current freeform collection.
+     * @returns The DocumentView of the intersected stroke.
+     */
     getInkIntersection = (): DocumentView | null => {
-        // this.children.filter((doc: Doc) => doc.type === DocumentType.INK)
-        // .forEach((doc: Doc) => {
-        //     const inkView = DocumentManager.Instance.getDocumentView(doc, this.props.CollectionView);
-        //     const ctrlPoints = inkView?.ComponentView?.inkScaledData().inkData.slice();
-        //     const array = [ctrlPoints[i], ctrlPoints[i + 1], ctrlPoints[i + 2], ctrlPoints[i + 3]];
-        //     const intersects = new Bezier(array.map(p => ({ x: p.X, y: p.Y }))).intersects({p1: {x: this._prevPoint.X, y: this._prevPoint.Y}, p2: {x: this._currPoint.X, y: this._currPoint.Y}});
-        //     if (intersects) return inkView;
-        // });
+        this.props.childDocuments?.filter(doc => doc.type === DocumentType.INK)
+            .forEach(doc => {
+                console.log("in for each");
+                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 intersects = new Bezier(array.map(p => ({ x: p.X, y: p.Y }))).intersects(
+                        { p1: { x: this._prevPoint.X, y: this._prevPoint.Y }, 
+                          p2: { x: this._currPoint.X, y: this._currPoint.Y } });
+                    if (intersects) return inkView;
+                }
+            });
         return null;
     }
 
-- 
cgit v1.2.3-70-g09d2


From 50f8a862bd27c212b13fcd1f626aafa5434e4842 Mon Sep 17 00:00:00 2001
From: Lauren <victor_kalev@brown.edu>
Date: Sat, 6 Nov 2021 15:32:47 -0400
Subject: from last

---
 .../views/collections/collectionFreeForm/CollectionFreeFormView.tsx  | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

(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 0941db5f8..0b7bf92cc 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -669,7 +669,10 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
      * @returns The DocumentView of the intersected stroke.
      */
     getInkIntersection = (): DocumentView | null => {
-        this.props.childDocuments?.filter(doc => doc.type === DocumentType.INK)
+        console.log("in intersection");
+        const currentCollection = this.props.CollectionFreeFormDocumentView?.().props.CollectionFreeFormView;
+        currentCollection?.childDocs
+            .filter(doc => doc.type === DocumentType.INK)
             .forEach(doc => {
                 console.log("in for each");
                 const inkView = DocumentManager.Instance.getDocumentView(doc, this.props.CollectionView);
-- 
cgit v1.2.3-70-g09d2


From b2b95106d21f03a0b2b46f972a36bbf00068ab20 Mon Sep 17 00:00:00 2001
From: Lauren <victor_kalev@brown.edu>
Date: Sat, 6 Nov 2021 16:49:36 -0400
Subject: eraser tool working

---
 .../collectionFreeForm/CollectionFreeFormView.tsx  | 46 +++++++++++++---------
 1 file changed, 27 insertions(+), 19 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 0b7bf92cc..f60af6355 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -605,9 +605,11 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
             this.removeMoveListeners();
             this.removeEndListeners();
             if (CurrentUserUtils.SelectedTool !== InkTool.None) {
-                this._deleteList.forEach(ink => {
-                    ink.props.removeDocument?.(ink.props.Document);
-                });
+                if (this._deleteList.length > 0) {
+                    this._deleteList.forEach(ink => {
+                        ink.props.removeDocument?.(ink.props.Document);
+                    });
+                }
                 this._prevPoint = this._currPoint = { X: -1, Y: -1 };
                 this._deleteList = [];
             }
@@ -641,12 +643,15 @@ 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 intersectStroke = this.getInkIntersection();
-            if (intersectStroke) {
-                if (!this._deleteList.includes(intersectStroke)) {
-                    this._deleteList.push(intersectStroke);
-                    // lower intersectStroke opacity to give user a visual indicator
-                }
+            const intersections: DocumentView[] = this.getInkIntersection();
+            if (intersections.length > 0) {
+                intersections.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;
+                    }
+                });
             }
         }
         if (InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE)) {
@@ -668,24 +673,27 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
      * Determines if the Eraser tool has intersected with an ink stroke in the current freeform collection.
      * @returns The DocumentView of the intersected stroke.
      */
-    getInkIntersection = (): DocumentView | null => {
-        console.log("in intersection");
-        const currentCollection = this.props.CollectionFreeFormDocumentView?.().props.CollectionFreeFormView;
-        currentCollection?.childDocs
+    getInkIntersection = (): DocumentView[] => {
+        const inks: DocumentView[] = [];
+        this.childDocs
             .filter(doc => doc.type === DocumentType.INK)
             .forEach(doc => {
-                console.log("in for each");
                 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 intersects = new Bezier(array.map(p => ({ x: p.X, y: p.Y }))).intersects(
-                        { p1: { x: this._prevPoint.X, y: this._prevPoint.Y }, 
-                          p2: { x: this._currPoint.X, y: this._currPoint.Y } });
-                    if (intersects) return inkView;
+                    // Converting from screen space to ink space for the intersection.
+                    const prevPointInkSpace = inkView?.ComponentView?.ptFromScreen?.(this._prevPoint);
+                    const currPointInkSpace = inkView?.ComponentView?.ptFromScreen?.(this._currPoint);
+                    if (prevPointInkSpace && currPointInkSpace) {
+                        const intersects = new Bezier(array.map(p => ({ x: p.X, y: p.Y }))).intersects(
+                            { p1: { x: prevPointInkSpace.X, y: prevPointInkSpace.Y }, 
+                              p2: { x: currPointInkSpace.X, y: currPointInkSpace.Y } });
+                        if (inkView && intersects.length > 0) inks.push(inkView);
+                    }
                 }
             });
-        return null;
+        return inks;
     }
 
     handle1PointerMove = (e: TouchEvent, me: InteractionUtils.MultiTouchEvent<TouchEvent>) => {
-- 
cgit v1.2.3-70-g09d2


From f450c8760c0563cdadd414f169902faeace585ec Mon Sep 17 00:00:00 2001
From: vkalev <victor_kalev@brown.edu>
Date: Tue, 9 Nov 2021 16:08:01 -0500
Subject: working on undo/redo

---
 src/client/views/InkStrokeProperties.ts              | 20 +++++++++++---------
 .../collectionFreeForm/CollectionFreeFormView.tsx    |  8 +++++++-
 2 files changed, 18 insertions(+), 10 deletions(-)

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

diff --git a/src/client/views/InkStrokeProperties.ts b/src/client/views/InkStrokeProperties.ts
index 02288bbb5..58d2992e0 100644
--- a/src/client/views/InkStrokeProperties.ts
+++ b/src/client/views/InkStrokeProperties.ts
@@ -356,16 +356,18 @@ export class InkStrokeProperties {
             const oldHandlePoint = ink[handleIndex];
             const oppositeHandlePoint = ink[oppositeHandleIndex];
             const controlPoint = ink[controlIndex];
-            const newHandlePoint = { X: ink[handleIndex].X - deltaX / xScale, Y: ink[handleIndex].Y - deltaY / yScale };
             const inkCopy = ink.slice();
-            inkCopy[handleIndex] = newHandlePoint;
-            const brokenIndices = Cast(doc.brokenInkIndices, listSpec("number"));
-            const equivIndex = closed ? (controlIndex === 0 ? ink.length - 1 : controlIndex === ink.length - 1 ? 0 : -1) : -1;
-            // Rotate opposite handle if user hasn't held 'Alt' key or not first/final control (which have only 1 handle).
-            if ((!brokenIndices || (!brokenIndices?.includes(controlIndex) && !brokenIndices?.includes(equivIndex))) &&
-                (closed || (handleIndex !== 1 && handleIndex !== ink.length - 2))) {
-                const angle = this.angleChange(oldHandlePoint, newHandlePoint, controlPoint);
-                inkCopy[oppositeHandleIndex] = this.rotatePoint(oppositeHandlePoint, controlPoint, angle);
+            if (!Number.isNaN(deltaX / xScale) && !Number.isNaN(deltaY / yScale)) {
+                const newHandlePoint = { X: ink[handleIndex].X - deltaX / xScale, Y: ink[handleIndex].Y - deltaY / yScale };
+                inkCopy[handleIndex] = newHandlePoint;
+                const brokenIndices = Cast(doc.brokenInkIndices, listSpec("number"));
+                const equivIndex = closed ? (controlIndex === 0 ? ink.length - 1 : controlIndex === ink.length - 1 ? 0 : -1) : -1;
+                // Rotate opposite handle if user hasn't held 'Alt' key or not first/final control (which have only 1 handle).
+                if ((!brokenIndices || (!brokenIndices?.includes(controlIndex) && !brokenIndices?.includes(equivIndex))) &&
+                    (closed || (handleIndex !== 1 && handleIndex !== ink.length - 2))) {
+                    const angle = this.angleChange(oldHandlePoint, newHandlePoint, controlPoint);
+                    inkCopy[oppositeHandleIndex] = this.rotatePoint(oppositeHandlePoint, controlPoint, angle);
+                }
             }
             return inkCopy;
         })
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index f60af6355..018f4f79a 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -29,7 +29,7 @@ import { SearchUtil } from "../../../util/SearchUtil";
 import { SelectionManager } from "../../../util/SelectionManager";
 import { SnappingManager } from "../../../util/SnappingManager";
 import { Transform } from "../../../util/Transform";
-import { undoBatch } from "../../../util/UndoManager";
+import { undoBatch, UndoManager } from "../../../util/UndoManager";
 import { COLLECTION_BORDER_WIDTH } from "../../../views/global/globalCssVariables.scss";
 import { Timeline } from "../../animationtimeline/Timeline";
 import { ContextMenu } from "../../ContextMenu";
@@ -99,6 +99,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
     private _layoutSizeData = observable.map<string, { width?: number, height?: number }>();
     private _cachedPool: Map<string, PoolData> = new Map();
     private _lastTap = 0;
+    private _batch: UndoManager.Batch | undefined = undefined;
 
     private get isAnnotationOverlay() { return this.props.isAnnotationOverlay; }
     private get scaleFieldKey() { return this.props.scaleField || "_viewScale"; }
@@ -449,6 +450,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
             document.addEventListener("pointerup", this.onPointerUp);
             // if not using a pen and in no ink mode
             if (CurrentUserUtils.SelectedTool === InkTool.None) {
+                this._batch = UndoManager.StartBatch("collectionErase");
                 this._downX = this._lastX = e.pageX;
                 this._downY = this._lastY = e.pageY;
             }
@@ -598,6 +600,8 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
         }
     }
 
+    // @undoBatch
+    @action
     onPointerUp = (e: PointerEvent): void => {
         if (!InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE)) {
             document.removeEventListener("pointermove", this.onPointerMove);
@@ -607,11 +611,13 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
             if (CurrentUserUtils.SelectedTool !== InkTool.None) {
                 if (this._deleteList.length > 0) {
                     this._deleteList.forEach(ink => {
+                        ink.Document.opacity = 1;
                         ink.props.removeDocument?.(ink.props.Document);
                     });
                 }
                 this._prevPoint = this._currPoint = { X: -1, Y: -1 };
                 this._deleteList = [];
+                this._batch?.end();
             }
         }
     }
-- 
cgit v1.2.3-70-g09d2


From 44c3ed6af9516c7bb91785934a997c7ab054ec5f Mon Sep 17 00:00:00 2001
From: vkalev <victor_kalev@brown.edu>
Date: Fri, 12 Nov 2021 16:54:51 -0500
Subject: undo works with ink deletion

---
 package-lock.json                                              |  6 +++---
 .../collections/collectionFreeForm/CollectionFreeFormView.tsx  | 10 ++++++----
 2 files changed, 9 insertions(+), 7 deletions(-)

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

diff --git a/package-lock.json b/package-lock.json
index d03f8af6b..128d6fba8 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -7709,14 +7709,14 @@
             "resolved": "https://registry.npmjs.org/image-size-stream/-/image-size-stream-1.1.0.tgz",
             "integrity": "sha1-Ivou2mbG31AQh0bacUkmSy0l+Gs=",
             "requires": {
-                "image-size": "image-size@git+https://github.com/netroy/image-size#da2c863807a3e9602617bdd357b0de3ab4a064c1",
+                "image-size": "github:netroy/image-size#da2c863807a3e9602617bdd357b0de3ab4a064c1",
                 "readable-stream": "^1.0.33",
                 "tryit": "^1.0.1"
             },
             "dependencies": {
                 "image-size": {
-                    "version": "git+ssh://git@github.com/netroy/image-size.git#da2c863807a3e9602617bdd357b0de3ab4a064c1",
-                    "from": "image-size@git+https://github.com/netroy/image-size#da2c863807a3e9602617bdd357b0de3ab4a064c1"
+                    "version": "github:netroy/image-size#da2c863807a3e9602617bdd357b0de3ab4a064c1",
+                    "from": "github:netroy/image-size#da2c863807a3e9602617bdd357b0de3ab4a064c1"
                 },
                 "isarray": {
                     "version": "0.0.1",
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 018f4f79a..bd72e4af9 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -99,7 +99,6 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
     private _layoutSizeData = observable.map<string, { width?: number, height?: number }>();
     private _cachedPool: Map<string, PoolData> = new Map();
     private _lastTap = 0;
-    private _batch: UndoManager.Batch | undefined = undefined;
 
     private get isAnnotationOverlay() { return this.props.isAnnotationOverlay; }
     private get scaleFieldKey() { return this.props.scaleField || "_viewScale"; }
@@ -450,7 +449,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
             document.addEventListener("pointerup", this.onPointerUp);
             // if not using a pen and in no ink mode
             if (CurrentUserUtils.SelectedTool === InkTool.None) {
-                this._batch = UndoManager.StartBatch("collectionErase");
+                // this._batch = UndoManager.StartBatch("collectionErase");
                 this._downX = this._lastX = e.pageX;
                 this._downY = this._lastY = e.pageY;
             }
@@ -600,7 +599,6 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
         }
     }
 
-    // @undoBatch
     @action
     onPointerUp = (e: PointerEvent): void => {
         if (!InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE)) {
@@ -610,14 +608,18 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
             this.removeEndListeners();
             if (CurrentUserUtils.SelectedTool !== InkTool.None) {
                 if (this._deleteList.length > 0) {
+                    // Ensuring ink opacity returns to normal if the user undos after deletion.
                     this._deleteList.forEach(ink => {
                         ink.Document.opacity = 1;
+                    });
+                    const batch = UndoManager.StartBatch("collectionErase");
+                    this._deleteList.forEach(ink => {
                         ink.props.removeDocument?.(ink.props.Document);
                     });
+                    batch.end();
                 }
                 this._prevPoint = this._currPoint = { X: -1, Y: -1 };
                 this._deleteList = [];
-                this._batch?.end();
             }
         }
     }
-- 
cgit v1.2.3-70-g09d2


From 9b5c863f7b88db579fba3ea8f85c43c2562f4289 Mon Sep 17 00:00:00 2001
From: vkalev <victor_kalev@brown.edu>
Date: Sat, 20 Nov 2021 14:11:50 -0500
Subject: fixing undo batch

---
 .../collectionFreeForm/CollectionFreeFormView.tsx  | 26 ++++++++++------------
 1 file changed, 12 insertions(+), 14 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 bd72e4af9..4447b7624 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -99,6 +99,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
     private _layoutSizeData = observable.map<string, { width?: number, height?: number }>();
     private _cachedPool: Map<string, PoolData> = new Map();
     private _lastTap = 0;
+    private _batch: UndoManager.Batch | undefined = undefined;
 
     private get isAnnotationOverlay() { return this.props.isAnnotationOverlay; }
     private get scaleFieldKey() { return this.props.scaleField || "_viewScale"; }
@@ -449,12 +450,12 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
             document.addEventListener("pointerup", this.onPointerUp);
             // if not using a pen and in no ink mode
             if (CurrentUserUtils.SelectedTool === InkTool.None) {
-                // this._batch = UndoManager.StartBatch("collectionErase");
                 this._downX = this._lastX = e.pageX;
                 this._downY = this._lastY = e.pageY;
             }
             // eraser plus anything else mode
             else {
+                this._batch = UndoManager.StartBatch("collectionErase");
                 this._prevPoint = { X: e.clientX, Y: e.clientY };
                 e.stopPropagation();
                 e.preventDefault();
@@ -608,18 +609,13 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
             this.removeEndListeners();
             if (CurrentUserUtils.SelectedTool !== InkTool.None) {
                 if (this._deleteList.length > 0) {
-                    // Ensuring ink opacity returns to normal if the user undos after deletion.
-                    this._deleteList.forEach(ink => {
-                        ink.Document.opacity = 1;
-                    });
-                    const batch = UndoManager.StartBatch("collectionErase");
                     this._deleteList.forEach(ink => {
                         ink.props.removeDocument?.(ink.props.Document);
                     });
-                    batch.end();
                 }
                 this._prevPoint = this._currPoint = { X: -1, Y: -1 };
                 this._deleteList = [];
+                this._batch?.end();
             }
         }
     }
@@ -653,7 +649,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
             this._currPoint = { X: e.clientX, Y: e.clientY };
             const intersections: DocumentView[] = this.getInkIntersection();
             if (intersections.length > 0) {
-                intersections.forEach(ink =>{
+                intersections.forEach(ink => {
                     if (!this._deleteList.includes(ink)) {
                         this._deleteList.push(ink);
                         // Lowering ink opacity to give the user a visual indicator of deletion.
@@ -695,8 +691,10 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
                     const currPointInkSpace = inkView?.ComponentView?.ptFromScreen?.(this._currPoint);
                     if (prevPointInkSpace && currPointInkSpace) {
                         const intersects = new Bezier(array.map(p => ({ x: p.X, y: p.Y }))).intersects(
-                            { p1: { x: prevPointInkSpace.X, y: prevPointInkSpace.Y }, 
-                              p2: { x: currPointInkSpace.X, y: currPointInkSpace.Y } });
+                            {
+                                p1: { x: prevPointInkSpace.X, y: prevPointInkSpace.Y },
+                                p2: { x: currPointInkSpace.X, y: currPointInkSpace.Y }
+                            });
                         if (inkView && intersects.length > 0) inks.push(inkView);
                     }
                 }
@@ -878,10 +876,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


From 7b3e1614fa645014c7de69a0f2057e389074c0ed Mon Sep 17 00:00:00 2001
From: vkalev <victor_kalev@brown.edu>
Date: Sat, 20 Nov 2021 15:38:54 -0500
Subject: updating intersections to map t-values to inks

---
 .../collectionFreeForm/CollectionFreeFormView.tsx  | 38 +++++++++++++---------
 1 file changed, 23 insertions(+), 15 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 4447b7624..6f79c5eab 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -647,15 +647,18 @@ 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: DocumentView[] = this.getInkIntersection();
-            if (intersections.length > 0) {
-                intersections.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 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;
+                        }
+                    });
+                }
             }
         }
         if (InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE)) {
@@ -675,10 +678,10 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
 
     /**
      * Determines if the Eraser tool has intersected with an ink stroke in the current freeform collection.
-     * @returns The DocumentView of the intersected stroke.
+     * @returns A dictionary mapping the t-value intersection of the eraser with a list of ink DocumentViews.
      */
-    getInkIntersection = (): DocumentView[] => {
-        const inks: DocumentView[] = [];
+    getEraserIntersections = (): { [id: number]: DocumentView[] } => {
+        const intersections: { [id: number]: DocumentView[] } = {};
         this.childDocs
             .filter(doc => doc.type === DocumentType.INK)
             .forEach(doc => {
@@ -690,16 +693,21 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
                     const prevPointInkSpace = inkView?.ComponentView?.ptFromScreen?.(this._prevPoint);
                     const currPointInkSpace = inkView?.ComponentView?.ptFromScreen?.(this._currPoint);
                     if (prevPointInkSpace && currPointInkSpace) {
-                        const intersects = new Bezier(array.map(p => ({ x: p.X, y: p.Y }))).intersects(
+                        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 && intersects.length > 0) inks.push(inkView);
+                        if (inkView && t) {
+                            t.forEach(val => {
+                                intersections[+t[0]] ? intersections[+t[0]].push(inkView) : intersections[+t[0]] = [inkView];
+                            });
+                        }
                     }
                 }
             });
-        return inks;
+        return intersections;
     }
 
     handle1PointerMove = (e: TouchEvent, me: InteractionUtils.MultiTouchEvent<TouchEvent>) => {
-- 
cgit v1.2.3-70-g09d2


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

---
 src/client/views/GestureOverlay.tsx                |   7 --
 .../collectionFreeForm/CollectionFreeFormView.tsx  | 136 +++++++++++++++++----
 src/fields/InkField.ts                             |   6 +
 3 files changed, 117 insertions(+), 32 deletions(-)

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

diff --git a/src/client/views/GestureOverlay.tsx b/src/client/views/GestureOverlay.tsx
index 99829139f..e2193c9ac 100644
--- a/src/client/views/GestureOverlay.tsx
+++ b/src/client/views/GestureOverlay.tsx
@@ -489,19 +489,12 @@ export class GestureOverlay extends Touchable {
         e.stopPropagation();
     }
 
-    @action
-    checkInkIntersection = (point: PointData) => {
-        
-    }
-
     @action
     onPointerDown = (e: React.PointerEvent) => {
         if (InteractionUtils.IsType(e, InteractionUtils.PENTYPE) || [InkTool.Highlighter, InkTool.Pen].includes(CurrentUserUtils.SelectedTool)) {
             this._points.push({ X: e.clientX, Y: e.clientY });
             setupMoveUpEvents(this, e, this.onPointerMove, this.onPointerUp, emptyFunction);
             // if (CurrentUserUtils.SelectedTool === InkTool.Highlighter) SetActiveInkColor("rgba(245, 230, 95, 0.75)");
-        } else if (InteractionUtils.IsType(e, InteractionUtils.ERASERTYPE)) {
-            this.checkInkIntersection({ X: e.clientX, Y: e.clientY });
         }
     }
 
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);
diff --git a/src/fields/InkField.ts b/src/fields/InkField.ts
index f16e143d8..1d50b5e0d 100644
--- a/src/fields/InkField.ts
+++ b/src/fields/InkField.ts
@@ -3,6 +3,7 @@ import { Scripting } from "../client/util/Scripting";
 import { Deserializable } from "../client/util/SerializationHelper";
 import { Copy, ToScriptString, ToString } from "./FieldSymbols";
 import { ObjectField } from "./ObjectField";
+import { DocumentView } from "../client/views/nodes/DocumentView";
 
 // Helps keep track of the current ink tool in use.
 export enum InkTool {
@@ -20,6 +21,11 @@ export interface PointData {
     Y: number;
 }
 
+export interface Intersection {
+    t: number;
+    ink: DocumentView;
+}
+
 // Defines an ink as an array of points.
 export type InkData = Array<PointData>;
 
-- 
cgit v1.2.3-70-g09d2


From cc7c677399d1aafa4078741922ffcd0b81ad720f Mon Sep 17 00:00:00 2001
From: vkalev <victor_kalev@brown.edu>
Date: Wed, 24 Nov 2021 12:45:50 -0500
Subject: segmentation of ink strokes prior to deletion

---
 .../collectionFreeForm/CollectionFreeFormView.tsx  | 192 ++++++++++++---------
 src/fields/InkField.ts                             |   9 +-
 2 files changed, 121 insertions(+), 80 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 ce4acb7ad..4aac49469 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, Intersection } from "../../../../fields/InkField";
+import { InkData, InkField, InkTool, PointData, Intersection, Segment } from "../../../../fields/InkField";
 import { List } from "../../../../fields/List";
 import { ObjectField } from "../../../../fields/ObjectField";
 import { RichTextField } from "../../../../fields/RichTextField";
@@ -53,6 +53,7 @@ import React = require("react");
 import { ColorScheme } from "../../../util/SettingsManager";
 import { Bezier } from "bezier-js";
 import { GestureOverlay } from "../../GestureOverlay";
+import { constants } from "perf_hooks";
 
 export const panZoomSchema = createSchema({
     _panX: "number",
@@ -648,6 +649,7 @@ 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 };
+            // Erasing ink strokes if intersections occur.
             const eraserIntersections: Intersection[] = this.getEraserIntersections();
             if (eraserIntersections) this.eraseInkStrokes(eraserIntersections);
         }
@@ -666,68 +668,28 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
         }
     }
 
-    @action
+    /**
+     * Iterates through all intersected ink strokes, determines their segmentation, draws back the non-intersected segments,
+     * and deletes the original stroke.
+     * @param eraserIntersections The intersections made by the eraser.
+     */
     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;
-                    }
+            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;
                 }
             }
         });
@@ -756,9 +718,19 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
                             p2: { x: currPointInkSpace.X, y: currPointInkSpace.Y }
                         });
                         if (inkView && intersects) {
-                            intersects.forEach(t => {
-                                intersections.push({ t: +t.toString(), ink: inkView });
-                            });
+                            for (const val of intersects) {
+                                // Casting t-value from type: (string | number) to number for comparisons.
+                                const t = +val.toString();
+                                var unique: boolean = true;
+                                // Ensuring there are no duplicate intersections in the list returned.
+                                for (const prevIntersect of intersections) {
+                                    if (prevIntersect.t === t) {
+                                        unique = false;
+                                        break;
+                                    }
+                                }
+                                if (unique) intersections.push({ t: +t.toString(), ink: inkView, curve: curve, index: i });
+                            }
                         }
                     }
                 }
@@ -766,36 +738,100 @@ 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());
-            });
+    /**
+     * 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.
+     * @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[] => {
+        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 })));
+            // 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];
+                //     }
+                // }
+            } else {
+                segment.push(curve);
+            }
         }
+        if (!excludeSegment) segments.push(segment);
+        return segments;
+    }
 
+    /**
+     * Determines all possible intersections of the current curve of the intersected ink stroke with all other curves of all
+     * ink strokes in the current collection.
+     * @param i The index of the current curve within the inkData of the intersected ink stroke.
+     * @param ink The intersected DocumentView of the ink stroke.
+     * @param curve The current curve of the intersected ink stroke.
+     * @returns A list of all t-values at which intersections occur at the current curve of the intersected ink stroke.
+     */
+    getInkIntersections = (i: number, ink: DocumentView, curve: Bezier): number[] => {
+        const tVals: number[] = [];
+        // Iterating through all ink strokes in the current freeform collection.
         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]];
+                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;
+                    // 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) {
-                        intersect.forEach(t => {
-                            intersections.push(+this.parseTValue(t.toString()));
+                    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);
                         });
                     }
                 }
             });
-        return intersections;
+        return tVals;
     }
 
-    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);
diff --git a/src/fields/InkField.ts b/src/fields/InkField.ts
index 1d50b5e0d..f61313674 100644
--- a/src/fields/InkField.ts
+++ b/src/fields/InkField.ts
@@ -4,6 +4,7 @@ import { Deserializable } from "../client/util/SerializationHelper";
 import { Copy, ToScriptString, ToString } from "./FieldSymbols";
 import { ObjectField } from "./ObjectField";
 import { DocumentView } from "../client/views/nodes/DocumentView";
+import { Bezier } from "bezier-js";
 
 // Helps keep track of the current ink tool in use.
 export enum InkTool {
@@ -22,10 +23,14 @@ export interface PointData {
 }
 
 export interface Intersection {
-    t: number;
-    ink: DocumentView;
+    t?: number;
+    ink?: DocumentView;
+    curve?: Bezier;
+    index?: number;
 }
 
+export type Segment = Array<Bezier>;
+
 // Defines an ink as an array of points.
 export type InkData = Array<PointData>;
 
-- 
cgit v1.2.3-70-g09d2


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

