aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/GestureOverlay.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/views/GestureOverlay.tsx')
-rw-r--r--src/client/views/GestureOverlay.tsx206
1 files changed, 73 insertions, 133 deletions
diff --git a/src/client/views/GestureOverlay.tsx b/src/client/views/GestureOverlay.tsx
index 5fddaec9a..777a34ebc 100644
--- a/src/client/views/GestureOverlay.tsx
+++ b/src/client/views/GestureOverlay.tsx
@@ -2,9 +2,9 @@ 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 { returnEmptyFilter, returnEmptyString, returnFalse, setupMoveUpEvents } from '../../ClientUtils';
+import { setupMoveUpEvents } from '../../ClientUtils';
import { emptyFunction, intersectRect } from '../../Utils';
-import { Doc, Opt, returnEmptyDoclist } from '../../fields/Doc';
+import { Doc } from '../../fields/Doc';
import { InkData, InkField, InkTool } from '../../fields/InkField';
import { NumCast } from '../../fields/Types';
import { Gestures } from '../../pen-gestures/GestureTypes';
@@ -14,27 +14,25 @@ import { DocumentType } from '../documents/DocumentTypes';
import { Docs } from '../documents/Documents';
import { InteractionUtils } from '../util/InteractionUtils';
import { ScriptingGlobals } from '../util/ScriptingGlobals';
-import { Transform } from '../util/Transform';
+import { SnappingManager } from '../util/SnappingManager';
import { undoable } from '../util/UndoManager';
import './GestureOverlay.scss';
import { InkingStroke } from './InkingStroke';
import { ObservableReactComponent } from './ObservableReactComponent';
-import { returnEmptyDocViewList } from './StyleProvider';
import { CollectionFreeFormView } from './collections/collectionFreeForm';
import {
- ActiveArrowEnd,
- ActiveArrowScale,
- ActiveArrowStart,
- ActiveDash,
- ActiveFillColor,
- ActiveInkBezierApprox,
+ ActiveInkArrowEnd,
+ ActiveInkArrowScale,
+ ActiveInkArrowStart,
ActiveInkColor,
+ ActiveInkDash,
+ ActiveInkFillColor,
ActiveInkWidth,
DocumentView,
- SetActiveArrowStart,
- SetActiveDash,
- SetActiveFillColor,
+ SetActiveInkArrowStart,
SetActiveInkColor,
+ SetActiveInkDash,
+ SetActiveInkFillColor,
SetActiveInkWidth,
} from './nodes/DocumentView';
export enum ToolglassTools {
@@ -57,20 +55,17 @@ export class GestureOverlay extends ObservableReactComponent<React.PropsWithChil
// eslint-disable-next-line no-use-before-define
static Instances: GestureOverlay[] = [];
- @observable public InkShape: Opt<Gestures> = undefined;
@observable public SavedColor?: string = undefined;
@observable public SavedWidth?: number = undefined;
@observable public Tool: ToolglassTools = ToolglassTools.None;
- @observable public KeepPrimitiveMode = false; // for whether primitive selection enters a one-shot or persistent mode
@observable private _thumbX?: number = undefined;
@observable private _thumbY?: number = undefined;
@observable private _pointerY?: number = undefined;
@observable private _points: { X: number; Y: number }[] = [];
- @observable private _strokes: InkData[] = [];
- @observable private _palette?: JSX.Element = undefined;
@observable private _clipboardDoc?: JSX.Element = undefined;
- @observable private _possibilities: JSX.Element[] = [];
+ @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);
@@ -96,8 +91,9 @@ export class GestureOverlay extends ObservableReactComponent<React.PropsWithChil
}
@action
onPointerDown = (e: React.PointerEvent) => {
+ (document.activeElement as HTMLElement)?.blur();
if (!(e.target as HTMLElement)?.className?.toString().startsWith('lm_')) {
- if ([InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(Doc.ActiveTool)) {
+ if (Doc.ActiveTool === InkTool.Ink) {
this._points.push({ X: e.clientX, Y: e.clientY });
setupMoveUpEvents(this, e, this.onPointerMove, this.onPointerUp, emptyFunction);
}
@@ -123,13 +119,9 @@ 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);
+ if (!SnappingManager.KeepGestureMode) {
+ SnappingManager.SetInkShape(undefined);
Doc.ActiveTool = InkTool.None;
- // SetActiveArrowStart('none');
- // SetActiveArrowEnd('none');
}
}
/**
@@ -180,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
@@ -192,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
@@ -209,8 +201,8 @@ export class GestureOverlay extends ObservableReactComponent<React.PropsWithChil
const intersectArray: string[] = [];
const scribbleBounds = InkField.getBounds(scribble);
for (let i = 0; i < scribble.length - 3; i += 4) { // for each segment of scribble
+ const scribbleSeg = InkField.Segment(scribble, i);
for (let j = 0; j < inkStroke.length - 3; j += 4) { // for each segment of ink stroke
- const scribbleSeg = InkField.Segment(scribble, i);
const strokeSeg = InkField.Segment(inkStroke, j);
const strokeBounds = InkField.getBounds(strokeSeg.points.map(pt => ({ X: pt.x, Y: pt.y })));
if (intersectRect(scribbleBounds, strokeBounds)) {
@@ -245,27 +237,29 @@ export class GestureOverlay extends ObservableReactComponent<React.PropsWithChil
this.dispatchGesture(Gestures.Stroke);
};
@action
- onPointerUp = () => {
- const ffView = DocumentView.DownDocView?.ComponentView instanceof CollectionFreeFormView && DocumentView.DownDocView.ComponentView;
- DocumentView.DownDocView = undefined;
+ onPointerUp = (e: PointerEvent) => {
+ 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 }));
const { Name, Score } =
- (this.InkShape
- ? new Result(this.InkShape, 1, Date.now)
+ (SnappingManager.InkShape
+ ? new Result(SnappingManager.InkShape, 1, Date.now)
: Doc.UserDoc().recognizeGestures && points.length > 2
? GestureUtils.GestureRecognizer.Recognize([points])
: undefined) ??
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) => {
switch (name) {
case Gestures.Line:
- if (cuspArray.length > 2) return undefined;
+ if (cuspArray.length > 2 && Score < 1) return undefined;
// eslint-disable-next-line no-fallthrough
case Gestures.Triangle:
case Gestures.Rectangle:
@@ -281,13 +275,17 @@ 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 {
+ ffView?._marqueeViewRef?.current?.setPreviewCursor?.(this._points[0].X, this._points[0].Y, false, false, undefined);
+ e.preventDefault();
}
+ this.primCreated();
this._points.length = 0;
};
/**
@@ -341,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]);
@@ -543,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);
@@ -553,97 +552,38 @@ export class GestureOverlay extends ObservableReactComponent<React.PropsWithChil
B.bottom += width / 2;
B.width += width;
B.height += width;
- const fillColor = ActiveFillColor();
+ const fillColor = ActiveInkFillColor();
const strokeColor = fillColor && fillColor !== 'transparent' ? fillColor : ActiveInkColor();
return [
this.props.children,
- 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);
- return (
- <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,
- b.left,
- b.top,
- strokeColor,
- width,
- width,
- 'miter',
- 'round',
- ActiveInkBezierApprox(),
- 'none' /* ActiveFillColor() */,
- ActiveArrowStart(),
- ActiveArrowEnd(),
- ActiveArrowScale(),
- ActiveDash(),
- 1,
- 1,
- this.InkShape as Gestures,
- 'none',
- 1.0,
- false
- )}
- </svg>
- );
- }),
- this._points.length <= 1 ? null : (
- <svg key="svg" 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(
- this._points.map(p => ({ X: p.X - (rect?.x || 0), Y: p.Y - (rect?.y || 0) })),
- B.left,
- B.top,
- ActiveInkColor(),
- width,
- width,
- 'miter',
- 'round',
- '',
- 'none' /* ActiveFillColor() */,
- ActiveArrowStart(),
- ActiveArrowEnd(),
- ActiveArrowScale(),
- ActiveDash(),
- 1,
- 1,
- this.InkShape as Gestures,
- 'none',
- 1.0,
- false
- )}
- </svg>
- ),
- ],
+ this._points.length <= 1 ? null : (
+ <svg key="svg" 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(
+ this._points.map(p => ({ X: p.X - (rect?.x || 0), Y: p.Y - (rect?.y || 0) })),
+ B.left,
+ B.top,
+ strokeColor,
+ width,
+ width,
+ 'miter',
+ 'round',
+ '',
+ 'none' /* ActiveFillColor() */,
+ ActiveInkArrowStart(),
+ ActiveInkArrowEnd(),
+ ActiveInkArrowScale(),
+ ActiveInkDash(),
+ 1,
+ 1,
+ SnappingManager.InkShape,
+ 'none',
+ 1.0,
+ false
+ )}
+ </svg>
+ ),
];
}
- screenToLocalTransform = () => new Transform(-(this._thumbX ?? 0), -(this._thumbY ?? 0) + this.height, 1);
- return300 = () => 300;
- @action
- public openFloatingDoc = (doc: Doc) => {
- this._clipboardDoc = (
- <DocumentView
- Document={doc}
- addDocument={undefined}
- addDocTab={returnFalse}
- pinToPres={emptyFunction}
- removeDocument={undefined}
- ScreenToLocalTransform={this.screenToLocalTransform}
- PanelWidth={this.return300}
- PanelHeight={this.return300}
- isDocumentActive={returnFalse}
- isContentActive={returnFalse}
- renderDepth={0}
- styleProvider={returnEmptyString}
- containerViewPath={returnEmptyDocViewList}
- focus={emptyFunction}
- whenChildContentsActiveChanged={emptyFunction}
- childFiltersByRanges={returnEmptyFilter}
- childFilters={returnEmptyFilter}
- searchFilterDocs={returnEmptyDoclist}
- />
- );
- };
@action
public closeFloatingDoc = () => {
@@ -654,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={{
@@ -690,10 +630,10 @@ ScriptingGlobals.add(function setPen(width: string, color: string, fill: string,
SetActiveInkColor(color);
GestureOverlay.Instance.SavedWidth = ActiveInkWidth();
SetActiveInkWidth(width);
- SetActiveFillColor(fill);
- SetActiveArrowStart(arrowStart);
- SetActiveArrowStart(arrowEnd);
- SetActiveDash(dash);
+ SetActiveInkFillColor(fill);
+ SetActiveInkArrowStart(arrowStart);
+ SetActiveInkArrowStart(arrowEnd);
+ SetActiveInkDash(dash);
});
});
// eslint-disable-next-line prefer-arrow-callback