diff options
Diffstat (limited to 'src/client/views/GestureOverlay.tsx')
-rw-r--r-- | src/client/views/GestureOverlay.tsx | 267 |
1 files changed, 131 insertions, 136 deletions
diff --git a/src/client/views/GestureOverlay.tsx b/src/client/views/GestureOverlay.tsx index 86b9f5e40..7246f0ba0 100644 --- a/src/client/views/GestureOverlay.tsx +++ b/src/client/views/GestureOverlay.tsx @@ -2,17 +2,8 @@ import * as fitCurve from 'fit-curve'; import { action, computed, makeObservable, observable, runInAction } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; -import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnEmptyString, returnFalse, setupMoveUpEvents } from '../../Utils'; -import { Doc, Opt } from '../../fields/Doc'; -import { InkData, InkTool } from '../../fields/InkField'; -import { BoolCast, NumCast } from '../../fields/Types'; -import MobileInkOverlay from '../../mobile/MobileInkOverlay'; -import { GestureUtils } from '../../pen-gestures/GestureUtils'; -import { MobileInkOverlayContent } from '../../server/Message'; -import { InteractionUtils } from '../util/InteractionUtils'; -import { ScriptingGlobals } from '../util/ScriptingGlobals'; -import { Transform } from '../util/Transform'; -import './GestureOverlay.scss'; +import { returnEmptyDoclist, returnEmptyFilter, returnEmptyString, returnFalse, setupMoveUpEvents } from '../../ClientUtils'; +import { emptyFunction } from '../../Utils'; import { ActiveArrowEnd, ActiveArrowScale, @@ -22,32 +13,44 @@ import { ActiveInkBezierApprox, ActiveInkColor, ActiveInkWidth, + Doc, + Opt, SetActiveArrowStart, SetActiveDash, SetActiveFillColor, SetActiveInkColor, SetActiveInkWidth, -} from './InkingStroke'; +} from '../../fields/Doc'; +import { InkData, InkTool } from '../../fields/InkField'; +import { NumCast } from '../../fields/Types'; +// import MobileInkOverlay from '../../mobile/MobileInkOverlay'; +import { Gestures } from '../../pen-gestures/GestureTypes'; +import { GestureUtils } from '../../pen-gestures/GestureUtils'; +// import { MobileInkOverlayContent } from '../../server/Message'; +import { InteractionUtils } from '../util/InteractionUtils'; +import { ScriptingGlobals } from '../util/ScriptingGlobals'; +import { Transform } from '../util/Transform'; +import './GestureOverlay.scss'; import { ObservableReactComponent } from './ObservableReactComponent'; -import { checkInksToGroup } from './global/globalScripts'; import { DocumentView } from './nodes/DocumentView'; +export enum ToolglassTools { + InkToText = 'inktotext', + IgnoreGesture = 'ignoregesture', + RadialMenu = 'radialmenu', + None = 'none', +} interface GestureOverlayProps { isActive: boolean; } @observer export class GestureOverlay extends ObservableReactComponent<React.PropsWithChildren<GestureOverlayProps>> { + // eslint-disable-next-line no-use-before-define static Instance: GestureOverlay; + // eslint-disable-next-line no-use-before-define static Instances: GestureOverlay[] = []; - public static set RecognizeGestures(active) { - Doc.UserDoc().recognizeGestures = active; - } - public static get RecognizeGestures() { - return BoolCast(Doc.UserDoc().recognizeGestures); - } - - @observable public InkShape: Opt<GestureUtils.Gestures> = undefined; + @observable public InkShape: Opt<Gestures> = undefined; @observable public SavedColor?: string = undefined; @observable public SavedWidth?: number = undefined; @observable public Tool: ToolglassTools = ToolglassTools.None; @@ -55,9 +58,6 @@ export class GestureOverlay extends ObservableReactComponent<React.PropsWithChil @observable private _thumbX?: number = undefined; @observable private _thumbY?: number = undefined; - @observable private _selectedIndex: number = -1; - @observable private _menuX: number = -300; - @observable private _menuY: number = -300; @observable private _pointerY?: number = undefined; @observable private _points: { X: number; Y: number }[] = []; @observable private _strokes: InkData[] = []; @@ -65,8 +65,6 @@ export class GestureOverlay extends ObservableReactComponent<React.PropsWithChil @observable private _clipboardDoc?: JSX.Element = undefined; @observable private _possibilities: JSX.Element[] = []; - public static DownDocView: DocumentView | undefined; - @computed private get height(): number { return 2 * Math.max(this._pointerY && this._thumbY ? this._thumbY - this._pointerY : 100, 100); } @@ -74,7 +72,7 @@ export class GestureOverlay extends ObservableReactComponent<React.PropsWithChil return this.Tool !== ToolglassTools.None; } - @observable private showMobileInkOverlay: boolean = false; + // @observable private showMobileInkOverlay: boolean = false; private _overlayRef = React.createRef<HTMLDivElement>(); private _d1: Doc | undefined; @@ -111,15 +109,14 @@ export class GestureOverlay extends ObservableReactComponent<React.PropsWithChil this._points.push({ X: e.clientX, Y: e.clientY }); if (this._points.length > 1) { - const B = this.svgBounds; const initialPoint = this._points[0]; const xInGlass = initialPoint.X > (this._thumbX ?? Number.MAX_SAFE_INTEGER) && initialPoint.X < (this._thumbX ?? Number.MAX_SAFE_INTEGER) + this.height; const yInGlass = initialPoint.Y > (this._thumbY ?? Number.MAX_SAFE_INTEGER) - this.height && initialPoint.Y < (this._thumbY ?? Number.MAX_SAFE_INTEGER); if (this.Tool !== ToolglassTools.None && xInGlass && yInGlass) { switch (this.Tool) { - case ToolglassTools.RadialMenu: - return true; - } + case ToolglassTools.RadialMenu: return true; + default: + } // prettier-ignore } } return false; @@ -128,21 +125,21 @@ export class GestureOverlay extends ObservableReactComponent<React.PropsWithChil @action primCreated() { if (!this.KeepPrimitiveMode) { this.InkShape = undefined; - //get out of ink mode after each stroke= - //if (Doc.ActiveTool === InkTool.Highlighter && GestureOverlay.Instance.SavedColor) SetActiveInkColor(GestureOverlay.Instance.SavedColor); + // get out of ink mode after each stroke= + // if (Doc.ActiveTool === InkTool.Highlighter && GestureOverlay.Instance.SavedColor) SetActiveInkColor(GestureOverlay.Instance.SavedColor); Doc.ActiveTool = InkTool.None; // SetActiveArrowStart('none'); // SetActiveArrowEnd('none'); } } @action - onPointerUp = (e: PointerEvent) => { - GestureOverlay.DownDocView = undefined; + onPointerUp = () => { + DocumentView.DownDocView = 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 })); - //if any of the shape is activated in the CollectionFreeFormViewChrome + // if any of the shape is activated in the CollectionFreeFormViewChrome if (this.InkShape) { this.makeBezierPolygon(this.InkShape, false); this.dispatchGesture(this.InkShape); @@ -151,20 +148,21 @@ export class GestureOverlay extends ObservableReactComponent<React.PropsWithChil // if we're not drawing in a toolglass try to recognize as gesture else { // need to decide when to turn gestures back on - const result = points.length > 2 && GestureUtils.GestureRecognizer.Recognize(new Array(points)); + const result = points.length > 2 && GestureUtils.GestureRecognizer.Recognize([points]); let actionPerformed = false; - if (GestureOverlay.RecognizeGestures && result && result.Score > 0.7) { + if (Doc.UserDoc().recognizeGestures && result && result.Score > 0.7) { switch (result.Name) { - case GestureUtils.Gestures.Line: - case GestureUtils.Gestures.Triangle: - case GestureUtils.Gestures.Rectangle: - case GestureUtils.Gestures.Circle: + case Gestures.Line: + case Gestures.Triangle: + case Gestures.Rectangle: + case Gestures.Circle: this.makeBezierPolygon(result.Name, true); actionPerformed = this.dispatchGesture(result.Name); break; - case GestureUtils.Gestures.Scribble: + case Gestures.Scribble: console.log('scribble'); break; + default: } } @@ -178,21 +176,20 @@ export class GestureOverlay extends ObservableReactComponent<React.PropsWithChil const controlPoints: { X: number; Y: number }[] = []; const bezierCurves = (fitCurve as any)(newPoints, 10); - for (const curve of bezierCurves) { + Array.from(bezierCurves).forEach((curve: any) => { controlPoints.push({ X: curve[0][0], Y: curve[0][1] }); controlPoints.push({ X: curve[1][0], Y: curve[1][1] }); controlPoints.push({ X: curve[2][0], Y: curve[2][1] }); controlPoints.push({ X: curve[3][0], Y: curve[3][1] }); - } + }); const dist = Math.sqrt( (controlPoints[0].X - controlPoints.lastElement().X) * (controlPoints[0].X - controlPoints.lastElement().X) + (controlPoints[0].Y - controlPoints.lastElement().Y) * (controlPoints[0].Y - controlPoints.lastElement().Y) ); + // eslint-disable-next-line prefer-destructuring if (controlPoints.length > 4 && dist < 10) controlPoints[controlPoints.length - 1] = controlPoints[0]; this._points.length = 0; this._points.push(...controlPoints); - this.dispatchGesture(GestureUtils.Gestures.Stroke); - // TODO: nda - check inks to group here - checkInksToGroup(); + this.dispatchGesture(Gestures.Stroke); } } } @@ -202,34 +199,34 @@ export class GestureOverlay extends ObservableReactComponent<React.PropsWithChil makeBezierPolygon = (shape: string, gesture: boolean) => { const xs = this._points.map(p => p.X); const ys = this._points.map(p => p.Y); - var right = Math.max(...xs); - var left = Math.min(...xs); - var bottom = Math.max(...ys); - var top = Math.min(...ys); + let right = Math.max(...xs); + let left = Math.min(...xs); + let bottom = Math.max(...ys); + let top = Math.min(...ys); const firstx = this._points[0].X; const firsty = this._points[0].Y; - var lastx = this._points[this._points.length - 2].X; - var lasty = this._points[this._points.length - 2].Y; - var fourth = (lastx - firstx) / 4; + let lastx = this._points[this._points.length - 2].X; + let lasty = this._points[this._points.length - 2].Y; + let fourth = (lastx - firstx) / 4; if (isNaN(fourth) || fourth === 0) { fourth = 0.01; } - var m = (lasty - firsty) / (lastx - firstx); + let m = (lasty - firsty) / (lastx - firstx); if (isNaN(m) || m === 0) { m = 0.01; } - const b = firsty - m * firstx; + // const b = firsty - m * firstx; if (shape === 'noRec') { return false; } if (!gesture) { - //if shape options is activated in inkOptionMenu - //take second to last point because _point[length-1] is _points[0] + // if shape options is activated in inkOptionMenu + // take second to last point because _point[length-1] is _points[0] right = this._points[this._points.length - 2].X; left = this._points[0].X; bottom = this._points[this._points.length - 2].Y; top = this._points[0].Y; - if (shape !== GestureUtils.Gestures.Arrow && shape !== GestureUtils.Gestures.Line && shape !== GestureUtils.Gestures.Circle) { + if (shape !== Gestures.Arrow && shape !== Gestures.Line && shape !== Gestures.Circle) { if (left > right) { const temp = right; right = left; @@ -244,7 +241,7 @@ export class GestureOverlay extends ObservableReactComponent<React.PropsWithChil } this._points.length = 0; switch (shape) { - case GestureUtils.Gestures.Rectangle: + case Gestures.Rectangle: this._points.push({ X: left, Y: top }); this._points.push({ X: left, Y: top }); this._points.push({ X: right, Y: top }); @@ -267,7 +264,7 @@ export class GestureOverlay extends ObservableReactComponent<React.PropsWithChil break; - case GestureUtils.Gestures.Triangle: + case Gestures.Triangle: this._points.push({ X: left, Y: bottom }); this._points.push({ X: left, Y: bottom }); @@ -285,39 +282,40 @@ export class GestureOverlay extends ObservableReactComponent<React.PropsWithChil this._points.push({ X: left, Y: bottom }); break; - case GestureUtils.Gestures.Circle: - // Approximation of a circle using 4 Bézier curves in which the constant "c" reduces the maximum radial drift to 0.019608%, - // making the curves indistinguishable from a circle. - // Source: https://spencermortensen.com/articles/bezier-circle/ - const c = 0.551915024494; - const centerX = (Math.max(left, right) + Math.min(left, right)) / 2; - const centerY = (Math.max(top, bottom) + Math.min(top, bottom)) / 2; - const radius = Math.max(centerX - Math.min(left, right), centerY - Math.min(top, bottom)); - - // Dividing the circle into four equal sections, and fitting each section to a cubic Bézier curve. - this._points.push({ X: centerX, Y: centerY + radius }); - this._points.push({ X: centerX + c * radius, Y: centerY + radius }); - this._points.push({ X: centerX + radius, Y: centerY + c * radius }); - this._points.push({ X: centerX + radius, Y: centerY }); - - this._points.push({ X: centerX + radius, Y: centerY }); - this._points.push({ X: centerX + radius, Y: centerY - c * radius }); - this._points.push({ X: centerX + c * radius, Y: centerY - radius }); - this._points.push({ X: centerX, Y: centerY - radius }); - - this._points.push({ X: centerX, Y: centerY - radius }); - this._points.push({ X: centerX - c * radius, Y: centerY - radius }); - this._points.push({ X: centerX - radius, Y: centerY - c * radius }); - this._points.push({ X: centerX - radius, Y: centerY }); - - this._points.push({ X: centerX - radius, Y: centerY }); - this._points.push({ X: centerX - radius, Y: centerY + c * radius }); - this._points.push({ X: centerX - c * radius, Y: centerY + radius }); - this._points.push({ X: centerX, Y: centerY + radius }); - + case Gestures.Circle: + { + // Approximation of a circle using 4 Bézier curves in which the constant "c" reduces the maximum radial drift to 0.019608%, + // making the curves indistinguishable from a circle. + // Source: https://spencermortensen.com/articles/bezier-circle/ + const c = 0.551915024494; + const centerX = (Math.max(left, right) + Math.min(left, right)) / 2; + const centerY = (Math.max(top, bottom) + Math.min(top, bottom)) / 2; + const radius = Math.max(centerX - Math.min(left, right), centerY - Math.min(top, bottom)); + + // Dividing the circle into four equal sections, and fitting each section to a cubic Bézier curve. + this._points.push({ X: centerX, Y: centerY + radius }); + this._points.push({ X: centerX + c * radius, Y: centerY + radius }); + this._points.push({ X: centerX + radius, Y: centerY + c * radius }); + this._points.push({ X: centerX + radius, Y: centerY }); + + this._points.push({ X: centerX + radius, Y: centerY }); + this._points.push({ X: centerX + radius, Y: centerY - c * radius }); + this._points.push({ X: centerX + c * radius, Y: centerY - radius }); + this._points.push({ X: centerX, Y: centerY - radius }); + + this._points.push({ X: centerX, Y: centerY - radius }); + this._points.push({ X: centerX - c * radius, Y: centerY - radius }); + this._points.push({ X: centerX - radius, Y: centerY - c * radius }); + this._points.push({ X: centerX - radius, Y: centerY }); + + this._points.push({ X: centerX - radius, Y: centerY }); + this._points.push({ X: centerX - radius, Y: centerY + c * radius }); + this._points.push({ X: centerX - c * radius, Y: centerY + radius }); + this._points.push({ X: centerX, Y: centerY + radius }); + } break; - case GestureUtils.Gestures.Line: + case Gestures.Line: if (Math.abs(firstx - lastx) < 10 && Math.abs(firsty - lasty) > 10) { lastx = firstx; } @@ -330,28 +328,32 @@ export class GestureOverlay extends ObservableReactComponent<React.PropsWithChil this._points.push({ X: lastx, Y: lasty }); this._points.push({ X: lastx, Y: lasty }); break; - case GestureUtils.Gestures.Arrow: - const x1 = left; - const y1 = top; - const x2 = right; - const y2 = bottom; - const L1 = Math.sqrt(Math.pow(Math.abs(x1 - x2), 2) + Math.pow(Math.abs(y1 - y2), 2)); - const L2 = L1 / 5; - const angle = 0.785398; - const x3 = x2 + (L2 / L1) * ((x1 - x2) * Math.cos(angle) + (y1 - y2) * Math.sin(angle)); - const y3 = y2 + (L2 / L1) * ((y1 - y2) * Math.cos(angle) - (x1 - x2) * Math.sin(angle)); - const x4 = x2 + (L2 / L1) * ((x1 - x2) * Math.cos(angle) - (y1 - y2) * Math.sin(angle)); - const y4 = y2 + (L2 / L1) * ((y1 - y2) * Math.cos(angle) + (x1 - x2) * Math.sin(angle)); - this._points.push({ X: x1, Y: y1 }); - this._points.push({ X: x2, Y: y2 }); - this._points.push({ X: x3, Y: y3 }); - this._points.push({ X: x4, Y: y4 }); - this._points.push({ X: x2, Y: y2 }); + case Gestures.Arrow: + { + const x1 = left; + const y1 = top; + const x2 = right; + const y2 = bottom; + const L1 = Math.sqrt(Math.abs(x1 - x2) ** 2 + Math.abs(y1 - y2) ** 2); + const L2 = L1 / 5; + const angle = 0.785398; + const x3 = x2 + (L2 / L1) * ((x1 - x2) * Math.cos(angle) + (y1 - y2) * Math.sin(angle)); + const y3 = y2 + (L2 / L1) * ((y1 - y2) * Math.cos(angle) - (x1 - x2) * Math.sin(angle)); + const x4 = x2 + (L2 / L1) * ((x1 - x2) * Math.cos(angle) - (y1 - y2) * Math.sin(angle)); + const y4 = y2 + (L2 / L1) * ((y1 - y2) * Math.cos(angle) + (x1 - x2) * Math.sin(angle)); + this._points.push({ X: x1, Y: y1 }); + this._points.push({ X: x2, Y: y2 }); + this._points.push({ X: x3, Y: y3 }); + this._points.push({ X: x4, Y: y4 }); + this._points.push({ X: x2, Y: y2 }); + } + break; + default: } return false; }; - dispatchGesture = (gesture: GestureUtils.Gestures, stroke?: InkData, text?: any) => { + dispatchGesture = (gesture: Gestures, stroke?: InkData, text?: any) => { const points = (stroke ?? this._points).slice(); return ( document.elementFromPoint(points[0].X, points[0].Y)?.dispatchEvent( @@ -384,14 +386,14 @@ export class GestureOverlay extends ObservableReactComponent<React.PropsWithChil } get elements() { - const selView = GestureOverlay.DownDocView; + const selView = DocumentView.DownDocView; 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); - B.left = B.left - width / 2; - B.right = B.right + width / 2; + const B = { left: -20000, right: 20000, top: -20000, bottom: 20000, width: 40000, height: 40000 }; // this.getBounds(this._points, true); + B.left -= width / 2; + B.right += width / 2; B.top = B.top - width / 2 - (rect?.y || 0); - B.bottom = B.bottom + width / 2; + B.bottom += width / 2; B.width += width; B.height += width; const fillColor = ActiveFillColor(); @@ -401,8 +403,9 @@ export class GestureOverlay extends ObservableReactComponent<React.PropsWithChil this._palette, [ this._strokes.map((l, i) => { - const b = { left: -20000, right: 20000, top: -20000, bottom: 20000, width: 40000, height: 40000 }; //this.getBounds(l, true); + const b = { left: -20000, right: 20000, top: -20000, bottom: 20000, width: 40000, height: 40000 }; // this.getBounds(l, true); return ( + // eslint-disable-next-line react/no-array-index-key <svg key={i} width={b.width} height={b.height} style={{ top: 0, left: 0, transform: `translate(${b.left}px, ${b.top}px)`, pointerEvents: 'none', position: 'absolute', zIndex: 30000, overflow: 'visible' }}> {InteractionUtils.CreatePolyline( l, @@ -414,7 +417,7 @@ export class GestureOverlay extends ObservableReactComponent<React.PropsWithChil 'miter', 'round', ActiveInkBezierApprox(), - 'none' /*ActiveFillColor()*/, + 'none' /* ActiveFillColor() */, ActiveArrowStart(), ActiveArrowEnd(), ActiveArrowScale(), @@ -441,7 +444,7 @@ export class GestureOverlay extends ObservableReactComponent<React.PropsWithChil 'miter', 'round', '', - 'none' /*ActiveFillColor()*/, + 'none' /* ActiveFillColor() */, ActiveArrowStart(), ActiveArrowEnd(), ActiveArrowScale(), @@ -491,15 +494,10 @@ export class GestureOverlay extends ObservableReactComponent<React.PropsWithChil this._clipboardDoc = undefined; }; - @action - enableMobileInkOverlay = (content: MobileInkOverlayContent) => { - this.showMobileInkOverlay = content.enableOverlay; - }; - render() { return ( <div className="gestureOverlay-cont" style={{ pointerEvents: this._props.isActive ? 'all' : 'none' }} ref={this._overlayRef} onPointerDown={this.onPointerDown}> - {this.showMobileInkOverlay ? <MobileInkOverlay /> : null} + {/* {this.showMobileInkOverlay ? <MobileInkOverlay /> : null} */} {this.elements} <div @@ -529,19 +527,14 @@ export class GestureOverlay extends ObservableReactComponent<React.PropsWithChil } } -// export class - -export enum ToolglassTools { - InkToText = 'inktotext', - IgnoreGesture = 'ignoregesture', - RadialMenu = 'radialmenu', - None = 'none', -} - ScriptingGlobals.add('GestureOverlay', GestureOverlay); +// eslint-disable-next-line prefer-arrow-callback ScriptingGlobals.add(function setToolglass(tool: any) { - runInAction(() => (GestureOverlay.Instance.Tool = tool)); + runInAction(() => { + GestureOverlay.Instance.Tool = tool; + }); }); +// eslint-disable-next-line prefer-arrow-callback ScriptingGlobals.add(function setPen(width: any, color: any, fill: any, arrowStart: any, arrowEnd: any, dash: any) { runInAction(() => { GestureOverlay.Instance.SavedColor = ActiveInkColor(); @@ -554,6 +547,7 @@ ScriptingGlobals.add(function setPen(width: any, color: any, fill: any, arrowSta SetActiveDash(dash); }); }); +// eslint-disable-next-line prefer-arrow-callback ScriptingGlobals.add(function resetPen() { runInAction(() => { SetActiveInkColor(GestureOverlay.Instance.SavedColor ?? 'rgb(0, 0, 0)'); @@ -561,8 +555,9 @@ ScriptingGlobals.add(function resetPen() { }); }, 'resets the pen tool'); ScriptingGlobals.add( + // eslint-disable-next-line prefer-arrow-callback function createText(text: any, x: any, y: any) { - GestureOverlay.Instance.dispatchGesture(GestureUtils.Gestures.Text, [{ X: x, Y: y }], text); + GestureOverlay.Instance.dispatchGesture(Gestures.Text, [{ X: x, Y: y }], text); }, 'creates a text document with inputted text and coordinates', '(text: any, x: any, y: any)' |