From 8680d1c31d4f835663c070f5b8cef57254e75e28 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Tue, 12 Nov 2024 16:42:35 -0500
Subject: made equation background same as text. fixed dflt stroke/link width
 to both be 1.  made function plot axis ranges get saved to Doc. marked
 equation->function links as being svgs.  fixed initial size of equation
 boxes..

---
 src/client/views/nodes/DocumentView.tsx | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

(limited to 'src/client/views/nodes/DocumentView.tsx')

diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 30f9e6363..92e98db87 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -459,10 +459,9 @@ export class DocumentViewInternal extends DocComponent<FieldViewProps & Document
                 }
                 if (annoData || this.Document !== linkdrag.linkSourceDoc.embedContainer) {
                     const dropDoc = annoData?.dropDocument ?? this._componentView?.getAnchor?.(true) ?? this.Document;
-                    const linkDoc = DocUtils.MakeLink(linkdrag.linkSourceDoc, dropDoc, {}, undefined, [de.x, de.y - 50]);
+                    const linkDoc = DocUtils.MakeLink(linkdrag.linkSourceDoc, dropDoc, {layout_isSvg: true }, undefined, [de.x, de.y - 50]);
                     if (linkDoc) {
                         de.complete.linkDocument = linkDoc;
-                        linkDoc.layout_isSvg = true;
                         DocumentView.linkCommonAncestor(linkDoc)?.ComponentView?.addDocument?.(linkDoc);
                     }
                 }
-- 
cgit v1.2.3-70-g09d2


From 196b92cb84095780d2b36244831cac03e9b66d8e Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Thu, 14 Nov 2024 10:48:16 -0500
Subject: changed isScribble related code to be more robust at determine cusps
 and deciding if cusps amount to a scribble. enabled undo of scribble erase
 without undoing scribble. added cusp visualizer for debugging, and cleaned up
 downDocView to  be downFfview on CollectionFreeformView.

---
 src/client/views/GestureOverlay.tsx                | 34 ++++++++++++----------
 .../collectionFreeForm/CollectionFreeFormView.tsx  |  7 +++++
 src/client/views/nodes/DocumentView.tsx            |  7 +----
 3 files changed, 27 insertions(+), 21 deletions(-)

(limited to 'src/client/views/nodes/DocumentView.tsx')

diff --git a/src/client/views/GestureOverlay.tsx b/src/client/views/GestureOverlay.tsx
index b0a750a9a..777a34ebc 100644
--- a/src/client/views/GestureOverlay.tsx
+++ b/src/client/views/GestureOverlay.tsx
@@ -64,6 +64,8 @@ export class GestureOverlay extends ObservableReactComponent<React.PropsWithChil
     @observable private _pointerY?: number = undefined;
     @observable private _points: { X: number; Y: number }[] = [];
     @observable private _clipboardDoc?: JSX.Element = undefined;
+    @observable private _debugCusps: { X: number; Y: number }[] = [];
+    @observable private _debugGestures = false;
 
     @computed private get height(): number {
         return 2 * Math.max(this._pointerY && this._thumbY ? this._thumbY - this._pointerY : 100, 100);
@@ -170,9 +172,9 @@ export class GestureOverlay extends ObservableReactComponent<React.PropsWithChil
      * Determines if what the array of cusp/intersection data corresponds to a scribble.
      * true if there are at least 4 cusps and either:
      *    1) the initial and final quarters of the array contain objects
-     *    2) or half of the cusps contain objects
+     *    2) or a declining percentage (ranges from 0.5 to 0.2 - based on the number of cusps) of cusp lines intersect strokes
      * @param intersectArray array of booleans coresponding to which scribble sections (regions separated by a cusp) contain Docs
-     * @returns
+     * @returns truthy if it's a scribble
      */
     determineIfScribble = (intersectArray: boolean[]) => {
         const quarterArrayLength = Math.ceil(intersectArray.length / 3.9); // use 3.9 instead of 4 to work better with strokes with only 4 cusps
@@ -182,7 +184,7 @@ export class GestureOverlay extends ObservableReactComponent<React.PropsWithChil
             }), { start: false, end: false }); // prettier-ignore
 
         const percentCuspsWithContent = intersectArray.filter(value => value).length / intersectArray.length;
-        return intersectArray.length > 3 && (percentCuspsWithContent >= 0.5 || (start && end));
+        return intersectArray.length > 3 && (percentCuspsWithContent >= Math.max(0.2, 1 / (intersectArray.length - 1)) || (start && end));
     };
     /**
      * determines if inks intersect
@@ -236,9 +238,8 @@ export class GestureOverlay extends ObservableReactComponent<React.PropsWithChil
     };
     @action
     onPointerUp = (e: PointerEvent) => {
-        const ffView = DocumentView.DownDocView?.ComponentView instanceof CollectionFreeFormView && DocumentView.DownDocView.ComponentView;
-        const downView = DocumentView.DownDocView;
-        DocumentView.DownDocView = undefined;
+        const ffView = CollectionFreeFormView.DownFfview;
+        CollectionFreeFormView.DownFfview = undefined;
         if (this._points.length > 1) {
             const B = this.svgBounds;
             const points = this._points.map(p => ({ X: p.X - B.left, Y: p.Y - B.top }));
@@ -251,6 +252,8 @@ export class GestureOverlay extends ObservableReactComponent<React.PropsWithChil
                 new Result(Gestures.Stroke, 1, Date.now); // prettier-ignore
 
             const cuspArray = this.getCusps(points);
+            const rect = this._overlayRef.current?.getBoundingClientRect();
+            this._debugCusps = rect ? cuspArray.map(p => ({ X: p.X + B.left - rect?.left, Y: p.Y + B.top - rect.top })) : [];
             // if any of the shape is activated in the CollectionFreeFormViewChrome
             // need to decide when to turn gestures back on
             const actionPerformed = ((name: Gestures) => {
@@ -272,14 +275,14 @@ export class GestureOverlay extends ObservableReactComponent<React.PropsWithChil
 
             if (!actionPerformed) {
                 const scribbledOver = ffView && this.isScribble(ffView, cuspArray, this._points);
+                this.dryInk();
                 if (scribbledOver) {
-                    undoable(() => ffView.removeDocument(scribbledOver), 'scribble erase')();
-                } else {
-                    this.dryInk();
+                    // can undo the erase without undoing the scribble, or undo a second time to undo the scribble
+                    setTimeout(undoable(() => ffView.removeDocument(scribbledOver.concat([ffView.childDocs.lastElement()])), 'scribble erase'));
                 }
             }
         } else {
-            (downView?.ComponentView as CollectionFreeFormView)?._marqueeViewRef?.current?.setPreviewCursor?.(this._points[0].X, this._points[0].Y, false, false, undefined);
+            ffView?._marqueeViewRef?.current?.setPreviewCursor?.(this._points[0].X, this._points[0].Y, false, false, undefined);
             e.preventDefault();
         }
         this.primCreated();
@@ -336,13 +339,14 @@ export class GestureOverlay extends ObservableReactComponent<React.PropsWithChil
     getCusps(points: InkData) {
         const arrayOfPoints: { X: number; Y: number }[] = [];
         arrayOfPoints.push(points[0]);
-        for (let i = 0; i < points.length - 2; i++) {
+        for (let i = 0; i < points.length - 4; i++) {
             const point1 = points[i];
-            const point2 = points[i + 1];
-            const point3 = points[i + 2];
+            const point2 = points[i + 2];
+            const point3 = points[i + 4];
             if (this.find_angle(point1, point2, point3) < 90) {
                 // NOTE: this is not an accurate way to find cusps -- it is highly dependent on sampling rate and doesn't work well with slowly drawn scribbles
                 arrayOfPoints.push(point2);
+                i += 2;
             }
         }
         arrayOfPoints.push(points[points.length - 1]);
@@ -538,7 +542,7 @@ export class GestureOverlay extends ObservableReactComponent<React.PropsWithChil
     }
 
     get elements() {
-        const selView = DocumentView.DownDocView;
+        const selView = CollectionFreeFormView.DownFfview;
         const width = Number(ActiveInkWidth()) * NumCast(selView?.Document._freeform_scale, 1); // * (selView?.screenToViewTransform().Scale || 1);
         const rect = this._overlayRef.current?.getBoundingClientRect();
         const B = { left: -20000, right: 20000, top: -20000, bottom: 20000, width: 40000, height: 40000 }; // this.getBounds(this._points, true);
@@ -590,7 +594,7 @@ export class GestureOverlay extends ObservableReactComponent<React.PropsWithChil
         return (
             <div className="gestureOverlay-cont" style={{ pointerEvents: this._props.isActive ? 'all' : 'none' }} ref={this._overlayRef} onPointerDown={this.onPointerDown}>
                 {this.elements}
-
+                {this._debugGestures && this._debugCusps.map(c => <div key={c.toString()} style={{ top: 0, left: 0, position: 'absolute', transform: `translate(${c.X}px, ${c.Y}px)`, width: 4, height: 4, background: 'red' }} />)}
                 <div
                     className="clipboardDoc-cont"
                     style={{
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 3de97f836..49b432d7c 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -98,6 +98,11 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
         const parent = CollectionFreeFormDocumentView.from(dv)?._props.reactParent;
         return parent instanceof CollectionFreeFormView ? parent : undefined;
     }
+    /**
+     * The Freeformview below the cursor at the start of a gesture (that receives the pointerDown event).  Used by GestureOverlay to determine the doc a gesture should apply to.
+     */
+    // eslint-disable-next-line no-use-before-define
+    public static DownFfview: CollectionFreeFormView | undefined; // the first DocView that receives a pointerdown event.  used by GestureOverlay to determine the doc a gesture should apply to.
 
     private _clusters = new CollectionFreeFormClusters(this);
     private _oldWheel: HTMLDivElement | null = null;
@@ -497,6 +502,8 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
 
     @action
     onPointerDown = (e: React.PointerEvent): void => {
+        if (!CollectionFreeFormView.DownFfview) CollectionFreeFormView.DownFfview = this;
+
         this._downX = this._lastX = e.pageX;
         this._downY = this._lastY = e.pageY;
         this._downTime = Date.now();
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 92e98db87..4bfa7fc92 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -355,7 +355,6 @@ export class DocumentViewInternal extends DocComponent<FieldViewProps & Document
     onPointerDown = (e: React.PointerEvent): void => {
         if (this._props.isGroupActive?.() === GroupActive.child && !this._props.isDocumentActive?.()) return;
         this._longPressSelector = setTimeout(() => SnappingManager.LongPress && this._props.select(false), 1000);
-        if (!DocumentView.DownDocView) DocumentView.DownDocView = this._docView;
 
         this._downX = e.clientX;
         this._downY = e.clientY;
@@ -459,7 +458,7 @@ export class DocumentViewInternal extends DocComponent<FieldViewProps & Document
                 }
                 if (annoData || this.Document !== linkdrag.linkSourceDoc.embedContainer) {
                     const dropDoc = annoData?.dropDocument ?? this._componentView?.getAnchor?.(true) ?? this.Document;
-                    const linkDoc = DocUtils.MakeLink(linkdrag.linkSourceDoc, dropDoc, {layout_isSvg: true }, undefined, [de.x, de.y - 50]);
+                    const linkDoc = DocUtils.MakeLink(linkdrag.linkSourceDoc, dropDoc, { layout_isSvg: true }, undefined, [de.x, de.y - 50]);
                     if (linkDoc) {
                         de.complete.linkDocument = linkDoc;
                         DocumentView.linkCommonAncestor(linkDoc)?.ComponentView?.addDocument?.(linkDoc);
@@ -1075,10 +1074,6 @@ export class DocumentView extends DocComponent<DocumentViewProps>() {
             ?.ComponentView?.updateIcon?.()
             .then(() => ImageCast(DocCast(doc).icon));
     }
-    /**
-     * The DocumentView below the cursor at the start of a gesture (that receives the pointerDown event).  Used by GestureOverlay to determine the doc a gesture should apply to.
-     */
-    public static DownDocView: DocumentView | undefined; // the first DocView that receives a pointerdown event.  used by GestureOverlay to determine the doc a gesture should apply to.
 
     public get displayName() {  return 'DocumentView(' + (this.Document?.title??"") + ')';  } // prettier-ignore
     private _htmlOverlayEffect: Opt<Doc>;
-- 
cgit v1.2.3-70-g09d2

