From 061dab5285d3a334a258d8097a6e95b065b30de3 Mon Sep 17 00:00:00 2001
From: bobzel <zzzman@gmail.com>
Date: Mon, 29 Nov 2021 14:09:17 -0500
Subject: added moving ink stroke segments.   added stretching and rotating ink
 strokes about opposite end point.

---
 src/client/views/InkControlPtHandles.tsx | 77 +++++++++++++++++++++++++++-----
 1 file changed, 67 insertions(+), 10 deletions(-)

(limited to 'src/client/views/InkControlPtHandles.tsx')

diff --git a/src/client/views/InkControlPtHandles.tsx b/src/client/views/InkControlPtHandles.tsx
index f24dab949..df803ba31 100644
--- a/src/client/views/InkControlPtHandles.tsx
+++ b/src/client/views/InkControlPtHandles.tsx
@@ -6,7 +6,7 @@ import { ControlPoint, InkData, PointData, InkField } from "../../fields/InkFiel
 import { List } from "../../fields/List";
 import { listSpec } from "../../fields/Schema";
 import { Cast, NumCast } from "../../fields/Types";
-import { setupMoveUpEvents } from "../../Utils";
+import { setupMoveUpEvents, returnFalse } from "../../Utils";
 import { Transform } from "../util/Transform";
 import { UndoManager } from "../util/UndoManager";
 import { Colors } from "./global/globalEnums";
@@ -133,7 +133,6 @@ export class InkControlPtHandles extends React.Component<InkControlProps> {
             inkCtrlPts.push({ ...inkData[i + 3], I: i + 3 });
         }
 
-        const screenSpaceLineWidth = this.props.screenSpaceLineWidth;
         const closed = InkingStroke.IsClosed(inkData);
         const nearestScreenPt = this.props.nearestScreenPt();
         const TagType = (broken?: boolean) => broken ? "rect" : "circle";
@@ -141,18 +140,18 @@ export class InkControlPtHandles extends React.Component<InkControlProps> {
             const broken = Cast(this.props.inkDoc.brokenInkIndices, listSpec("number"))?.includes(control.I);
             const Tag = TagType((control.I === 0 || control.I === inkData.length - 1) && !closed) as keyof JSX.IntrinsicElements;
             return <Tag key={control.I.toString() + scale}
-                x={control.X - screenSpaceLineWidth * 2 * scale}
-                y={control.Y - screenSpaceLineWidth * 2 * scale}
+                x={control.X - this.props.screenSpaceLineWidth * 2 * scale}
+                y={control.Y - this.props.screenSpaceLineWidth * 2 * scale}
                 cx={control.X}
                 cy={control.Y}
-                r={screenSpaceLineWidth * 2 * scale}
+                r={this.props.screenSpaceLineWidth * 2 * scale}
                 opacity={this.controlUndo ? 0.15 : 1}
-                height={screenSpaceLineWidth * 4 * scale}
-                width={screenSpaceLineWidth * 4 * scale}
-                strokeWidth={screenSpaceLineWidth / 2}
+                height={this.props.screenSpaceLineWidth * 4 * scale}
+                width={this.props.screenSpaceLineWidth * 4 * scale}
+                strokeWidth={this.props.screenSpaceLineWidth / 2}
                 stroke={Colors.MEDIUM_BLUE}
                 fill={broken ? Colors.MEDIUM_BLUE : color}
-                onPointerDown={(e: any) => this.onControlDown(e, control.I)}
+                onPointerDown={(e: React.PointerEvent) => this.onControlDown(e, control.I)}
                 onMouseEnter={() => this.onEnterControl(control.I)}
                 onMouseLeave={this.onLeaveControl}
                 pointerEvents="all"
@@ -164,7 +163,7 @@ export class InkControlPtHandles extends React.Component<InkControlProps> {
                 <circle key={"npt"}
                     cx={nearestScreenPt.X}
                     cy={nearestScreenPt.Y}
-                    r={screenSpaceLineWidth * 2}
+                    r={this.props.screenSpaceLineWidth * 2}
                     fill={"#00007777"}
                     stroke={"#00007777"}
                     strokeWidth={0}
@@ -175,4 +174,62 @@ export class InkControlPtHandles extends React.Component<InkControlProps> {
         </svg>
         );
     }
+}
+
+
+export interface InkEndProps {
+    inkDoc: Doc;
+    inkView: DocumentView;
+    screenSpaceLineWidth: number;
+    startPt: PointData;
+    endPt: PointData;
+}
+@observer
+export class InkEndPtHandles extends React.Component<InkEndProps> {
+    @observable controlUndo: UndoManager.Batch | undefined;
+    @observable _overStart: boolean = false;
+    @observable _overEnd: boolean = false;
+
+    @action
+    dragRotate = (e: React.PointerEvent, p1: () => { X: number, Y: number }, p2: () => { X: number, Y: number }) => {
+        setupMoveUpEvents(this, e, (e) => {
+            if (!this.controlUndo) this.controlUndo = UndoManager.StartBatch("stretch ink");
+            // compute stretch factor by finding scaling along axis between start and end points
+            const v1 = { x: p1().X - p2().X, y: p1().Y - p2().Y };
+            const v2 = { x: e.clientX - p2().X, y: e.clientY - p2().Y };
+            const v1len = Math.sqrt(v1.x * v1.x + v1.y * v1.y);
+            const v2len = Math.sqrt(v2.x * v2.x + v2.y * v2.y);
+            const scaling = v2len / v1len;
+            const v1n = { x: v1.x / v1len, y: v1.y / v1len };
+            const v2n = { x: v2.x / v2len, y: v2.y / v2len };
+            const angle = Math.acos(v1n.x * v2n.x + v1n.y * v2n.y) * Math.sign(v1.x * v2.y - v2.x * v1.y)
+            InkStrokeProperties.Instance?.stretchInk([this.props.inkView], scaling, { x: p2().X, y: p2().Y }, v1n);
+            InkStrokeProperties.Instance?.rotateInk([this.props.inkView], angle, { x: p2().X, y: p2().Y });
+            return false;
+        }, action(() => {
+            this.controlUndo?.end();
+            this.controlUndo = undefined;
+            UndoManager.FilterBatches(["data", "x", "y", "width", "height"]);
+        }), returnFalse);
+    }
+
+    render() {
+        const hdl = (pt: PointData, dragFunc: (e: React.PointerEvent) => void) => <circle key={"npt"}
+            cx={pt.X}
+            cy={pt.Y}
+            r={this.props.screenSpaceLineWidth * 2}
+            fill={this._overStart ? "#aaaaaa" : "#99999977"}
+            stroke={"#00007777"}
+            strokeWidth={0}
+            onPointerLeave={action(() => this._overStart = false)}
+            onPointerEnter={action(() => this._overStart = true)}
+            onPointerDown={e => dragFunc(e)}
+            pointerEvents="all"
+        />;
+        return (<svg>
+            {hdl(this.props.startPt, (e: React.PointerEvent) => this.dragRotate(e, () => this.props.startPt, () => this.props.endPt))}
+            {hdl(this.props.endPt, (e: React.PointerEvent) => this.dragRotate(e, () => this.props.endPt, () => this.props.startPt))}
+        </svg>
+        );
+    }
 }
\ No newline at end of file
-- 
cgit v1.2.3-70-g09d2

