aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/collections
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/views/collections')
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx106
-rw-r--r--src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx2
-rw-r--r--src/client/views/collections/collectionFreeForm/MarqueeView.tsx80
3 files changed, 128 insertions, 60 deletions
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index a298e6ac4..8575807b3 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -548,24 +548,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
default: {
const { points } = ge;
const B = this.screenToFreeformContentsXf.transformBounds(ge.bounds.left, ge.bounds.top, ge.bounds.width, ge.bounds.height);
- const inkWidth = ActiveInkWidth() * this.ScreenToLocalBoxXf().Scale;
- const inkDoc = Docs.Create.InkDocument(
- points,
- { title: ge.gesture.toString(),
- x: B.x - inkWidth / 2,
- y: B.y - inkWidth / 2,
- _width: B.width + inkWidth,
- _height: B.height + inkWidth,
- stroke_showLabel: BoolCast(Doc.UserDoc().activeInkHideTextLabels)}, // prettier-ignore
- inkWidth,
- ActiveInkColor(),
- ActiveInkBezierApprox(),
- ActiveFillColor(),
- ActiveArrowStart(),
- ActiveArrowEnd(),
- ActiveDash(),
- ActiveIsInkMask()
- );
+ const inkDoc = this.createInkDoc(points, B);
if (Doc.ActiveTool === InkTool.Write) {
this.unprocessedDocs.push(inkDoc);
CollectionFreeFormView.collectionsWithUnprocessedInk.add(this);
@@ -652,26 +635,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
const segments = this.segmentErase(intersect.inkView, intersect.t); // intersect.t is where the eraser intersected the ink stroke - want to remove the segment that starts at the intersection just before this t value and goes to the one just after it
const newStrokes = segments?.map(segment => {
const points = segment.reduce((data, curve) => [...data, ...curve.points.map(p => intersect.inkView.ComponentView?.ptToScreen?.({ X: p.x, Y: p.y }) ?? { X: 0, Y: 0 })], [] as PointData[]);
- const bounds = InkField.getBounds(points);
- const B = this.screenToFreeformContentsXf.transformBounds(bounds.left, bounds.top, bounds.width, bounds.height);
- const inkWidth = ActiveInkWidth() * this.ScreenToLocalBoxXf().Scale;
- return Docs.Create.InkDocument(
- points,
- { title: 'stroke',
- x: B.x - inkWidth / 2,
- y: B.y - inkWidth / 2,
- _width: B.width + inkWidth,
- _height: B.height + inkWidth,
- stroke_showLabel: BoolCast(Doc.UserDoc().activeInkHideTextLabels)}, // prettier-ignore
- inkWidth,
- ActiveInkColor(),
- ActiveInkBezierApprox(),
- ActiveFillColor(),
- ActiveArrowStart(),
- ActiveArrowEnd(),
- ActiveDash(),
- ActiveIsInkMask()
- );
+ return this.createInkDoc(points);
});
newStrokes && this.addDocument?.(newStrokes);
// setTimeout(() => this._eraserLock--);
@@ -1276,18 +1240,43 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
return tVals;
};
+ createInkDoc = (points: InkData, transformedBounds?: { x: number; y: number; width: number; height: number }) => {
+ const bounds = InkField.getBounds(points);
+ const B = transformedBounds || this.screenToFreeformContentsXf.transformBounds(bounds.left, bounds.top, bounds.width, bounds.height);
+ const inkWidth = ActiveInkWidth() * this.ScreenToLocalBoxXf().Scale;
+ return Docs.Create.InkDocument(
+ points,
+ { title: 'stroke',
+ x: B.x - inkWidth / 2,
+ y: B.y - inkWidth / 2,
+ _width: B.width + inkWidth,
+ _height: B.height + inkWidth,
+ stroke_showLabel: BoolCast(Doc.UserDoc().activeInkHideTextLabels)}, // prettier-ignore
+ inkWidth,
+ ActiveInkColor(),
+ ActiveInkBezierApprox(),
+ ActiveFillColor(),
+ ActiveArrowStart(),
+ ActiveArrowEnd(),
+ ActiveDash(),
+ ActiveIsInkMask()
+ );
+ };
+
@action
- showSmartDraw = (e: PointerEvent, doubleTap?: boolean) => {
- SmartDrawHandler.Instance.displaySmartDrawHandler(e.pageX, e.pageY, this.createDrawing, this.removeDrawing);
+ showSmartDraw = (e: PointerEvent) => {
+ SmartDrawHandler.Instance.CreateDrawingDoc = this.createDrawingDoc;
+ SmartDrawHandler.Instance.RemoveDrawing = this.removeDrawing;
+ SmartDrawHandler.Instance.AddDrawing = this.addDrawing;
+ SmartDrawHandler.Instance.displaySmartDrawHandler(e.pageX, e.pageY);
};
_drawing: Doc[] = [];
_drawingContainer: Doc | undefined = undefined;
@undoBatch
- createDrawing = (strokeData: [InkData, string, string][], opts: DrawingOptions, gptRes: string, containerDoc?: Doc) => {
+ createDrawingDoc = (strokeData: [InkData, string, string][], opts: DrawingOptions, gptRes: string) => {
this._drawing = [];
const xf = this.screenToFreeformContentsXf;
- // this._drawingContainer = undefined;
strokeData.forEach((stroke: [InkData, string, string]) => {
const bounds = InkField.getBounds(stroke[0]);
const B = xf.transformBounds(bounds.left, bounds.top, bounds.width, bounds.height);
@@ -1310,20 +1299,9 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
ActiveIsInkMask()
);
this._drawing.push(inkDoc);
- this.addDocument(inkDoc);
});
- const collection = this._marqueeViewRef.current?.collection(undefined, true, this._drawing);
- if (collection) {
- const docData = collection[DocData];
- docData.title = opts.text.match(/^(.*?)~~~.*$/)?.[1] || opts.text;
- docData.drawingInput = opts.text;
- docData.drawingComplexity = opts.complexity;
- docData.drawingColored = opts.autoColor;
- docData.drawingSize = opts.size;
- docData.drawingData = gptRes;
- this._drawingContainer = collection;
- }
- this._batch?.end();
+ const collection = MarqueeView.getCollection(this._drawing, undefined, true, { left: 1, top: 1, width: 1, height: 1 });
+ return collection;
};
removeDrawing = (doc?: Doc) => {
@@ -1339,6 +1317,19 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
this._drawing = [];
};
+ addDrawing = (doc: Doc, opts: DrawingOptions, gptRes: string) => {
+ const docData = doc[DocData];
+ docData.title = opts.text.match(/^(.*?)~~~.*$/)?.[1] || opts.text;
+ docData.drawingInput = opts.text;
+ docData.drawingComplexity = opts.complexity;
+ docData.drawingColored = opts.autoColor;
+ docData.drawingSize = opts.size;
+ docData.drawingData = gptRes;
+ this._drawingContainer = doc;
+ this.addDocument(doc);
+ this._batch?.end();
+ };
+
@action
zoom = (pointX: number, pointY: number, deltaY: number): void => {
if (this.Document.isGroup || this.Document[(this._props.viewField ?? '_') + 'freeform_noZoom']) return;
@@ -2045,7 +2036,10 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
optionItems.push({
description: 'Show Drawing Editor',
event: action(() => {
- !SmartDrawHandler.Instance._showRegenerate ? SmartDrawHandler.Instance.displayRegenerate(this._downX, this._downY - 10, this.createDrawing, this.removeDrawing) : SmartDrawHandler.Instance.hideRegenerate();
+ SmartDrawHandler.Instance.CreateDrawingDoc = this.createDrawingDoc;
+ SmartDrawHandler.Instance.AddDrawing = this.addDrawing;
+ SmartDrawHandler.Instance.RemoveDrawing = this.removeDrawing;
+ !SmartDrawHandler.Instance._showRegenerate ? SmartDrawHandler.Instance.displayRegenerate(this._downX, this._downY - 10) : SmartDrawHandler.Instance.hideRegenerate();
}),
icon: 'pen-to-square',
});
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx b/src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx
index b3fdd9379..76c37dff0 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx
@@ -21,6 +21,7 @@ export class MarqueeOptionsMenu extends AntimodeMenu<AntimodeMenuProps> {
public pinWithView: (e: KeyboardEvent | React.PointerEvent | undefined) => void = unimplementedFunction;
public classifyImages: (e: React.MouseEvent | undefined) => void = unimplementedFunction;
public groupImages: () => void = unimplementedFunction;
+ public smoothStrokes: (docs?: Doc[]) => void = unimplementedFunction;
public isShown = () => this._opacity > 0;
constructor(props: any) {
super(props);
@@ -41,6 +42,7 @@ export class MarqueeOptionsMenu extends AntimodeMenu<AntimodeMenuProps> {
<IconButton tooltip="Delete Documents" onPointerDown={this.delete} icon={<FontAwesomeIcon icon="trash-alt" />} color={this.userColor} />
<IconButton tooltip="Pin selected region" onPointerDown={this.pinWithView} icon={<FontAwesomeIcon icon="map-pin" />} color={this.userColor} />
<IconButton tooltip="Classify Images" onPointerDown={this.classifyImages} icon={<FontAwesomeIcon icon="object-group" />} color={this.userColor} />
+ <IconButton tooltip="Smooth Strokes" onPointerDown={() => this.smoothStrokes} icon={<FontAwesomeIcon icon="object-group" />} color={this.userColor} />
</>
);
return this.getElement(buttons);
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index 8560323c9..92c0da983 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -4,14 +4,14 @@ import { action, computed, makeObservable, observable } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
import { ClientUtils, lightOrDark, returnFalse } from '../../../../ClientUtils';
-import { intersectRect, numberRange } from '../../../../Utils';
+import { intersectRect, numberRange, unimplementedFunction } from '../../../../Utils';
import { Doc, NumListCast, Opt } from '../../../../fields/Doc';
import { AclAdmin, AclAugment, AclEdit, DocData } from '../../../../fields/DocSymbols';
import { Id } from '../../../../fields/FieldSymbols';
import { InkData, InkField, InkTool } from '../../../../fields/InkField';
import { List } from '../../../../fields/List';
import { RichTextField } from '../../../../fields/RichTextField';
-import { Cast, FieldValue, ImageCast, NumCast, StrCast } from '../../../../fields/Types';
+import { BoolCast, Cast, DocCast, FieldValue, ImageCast, NumCast, StrCast } from '../../../../fields/Types';
import { ImageField } from '../../../../fields/URLField';
import { GetEffectiveAcl } from '../../../../fields/util';
import { gptGetEmbedding, gptImageLabel } from '../../../apis/gpt/GPT';
@@ -26,7 +26,7 @@ import { ContextMenu } from '../../ContextMenu';
import { ObservableReactComponent } from '../../ObservableReactComponent';
import { MarqueeViewBounds } from '../../PinFuncs';
import { PreviewCursor } from '../../PreviewCursor';
-import { DocumentView } from '../../nodes/DocumentView';
+import { ActiveArrowEnd, ActiveArrowStart, ActiveDash, ActiveFillColor, ActiveInkBezierApprox, ActiveInkColor, ActiveInkWidth, ActiveIsInkMask, DocumentView } from '../../nodes/DocumentView';
import { OpenWhere } from '../../nodes/OpenWhere';
import { pasteImageBitmap } from '../../nodes/WebBoxRenderer';
import { FormattedTextBox } from '../../nodes/formattedText/FormattedTextBox';
@@ -36,7 +36,12 @@ import { CollectionFreeFormView } from './CollectionFreeFormView';
import { ImageLabelHandler } from './ImageLabelHandler';
import { MarqueeOptionsMenu } from './MarqueeOptionsMenu';
import './MarqueeView.scss';
-import { collectionOf } from '@turf/turf';
+import { collectionOf, points } from '@turf/turf';
+import { InkingStroke } from '../../InkingStroke';
+import { GestureUtils } from '../../../../pen-gestures/GestureUtils';
+import { Gestures } from '../../../../pen-gestures/GestureTypes';
+import { GestureOverlay } from '../../GestureOverlay';
+import { InkStrokeProperties } from '../../InkStrokeProperties';
interface MarqueeViewProps {
getContainerTransform: () => Transform;
@@ -89,6 +94,8 @@ export class MarqueeView extends ObservableReactComponent<SubCollectionViewProps
return bounds;
}
+ public AddInkDoc: (points: InkData) => Doc | void = unimplementedFunction;
+
componentDidMount() {
this._props.setPreviewCursor?.(this.setPreviewCursor);
}
@@ -277,6 +284,7 @@ export class MarqueeView extends ObservableReactComponent<SubCollectionViewProps
MarqueeOptionsMenu.Instance.pinWithView = this.pinWithView;
MarqueeOptionsMenu.Instance.classifyImages = this.classifyImages;
MarqueeOptionsMenu.Instance.groupImages = this.groupImages;
+ MarqueeOptionsMenu.Instance.smoothStrokes = this.smoothStrokes;
document.addEventListener('pointerdown', hideMarquee, true);
document.addEventListener('wheel', hideMarquee, true);
} else {
@@ -490,6 +498,70 @@ export class MarqueeView extends ObservableReactComponent<SubCollectionViewProps
});
@undoBatch
+ smoothStrokes = action((docs?: Doc[]) => {
+ docs && docs.length > 0 ? (this._selectedDocs = docs) : (this._selectedDocs = this.marqueeSelect(false, DocumentType.INK));
+ if (this._selectedDocs.length == 0) return;
+
+ this._selectedDocs.forEach(stroke => {
+ const docView = DocumentView.getDocumentView(stroke);
+ const inkStroke = docView?.ComponentView as InkingStroke;
+ const { inkData } = inkStroke.inkScaledData();
+
+ const result = inkData.length > 2 && GestureUtils.GestureRecognizer.Recognize([inkData]);
+ console.log(result);
+ let polygonPoints: { X: number; Y: number }[] | undefined = undefined;
+ if (result && (result.Name === 'line' ? result.Score > 0.9 : result.Score > 0.8)) {
+ switch (result.Name) {
+ case Gestures.Line:
+ case Gestures.Triangle:
+ case Gestures.Rectangle:
+ case Gestures.Circle:
+ GestureOverlay.makeBezierPolygon(inkData, result.Name, true);
+ break;
+ default:
+ }
+ } else {
+ const distances: number[] = [];
+ for (var i = 0; i < inkData.length - 3; i += 4) {
+ distances.push(Math.sqrt((inkData[i].X - inkData[i + 3].X) ** 2 + (inkData[i].Y - inkData[i + 3].Y) ** 2));
+ }
+ const avgDist = (NumCast(stroke.width) + NumCast(stroke.height)) / 2;
+ // const avgDist = distances.reduce((a, b) => a + b) / distances.length;
+ if (Math.sqrt((inkData.lastElement().X - inkData[0].X) ** 2 + (inkData.lastElement().Y - inkData[0].Y) ** 2) < avgDist) {
+ inkData.pop();
+ inkData.push({ X: inkData[0].X, Y: inkData[0].Y });
+ }
+ // const editedPoints: InkData = [];
+ // const toDelete: number[] = [];
+
+ // distances.forEach((dist, i) => {
+ // if (dist < avgDist / 3) {
+ // toDelete.unshift(i * 4);
+ // }
+ // });
+ // toDelete.forEach(pt => {
+ // InkStrokeProperties.Instance._currentPoint = pt;
+ // docView && InkStrokeProperties.Instance.deletePoints(docView, false);
+ // });
+
+ // for (var i = 0; i < distances.length; i++) {
+ // if (distances[i] > avgDist / 3) {
+ // editedPoints.push(...inkData.slice(i * 4, i * 4 + 4));
+ // } else {
+ // if (i !== distances.length) {
+ // editedPoints.push(...inkData.slice(i * 4, i * 4 + 2));
+ // editedPoints.push(...inkData.slice(i * 4 + 6, i * 4 + 8));
+ // i++;
+ // }
+ // }
+ // }
+ // inkData.length = 0;
+ // inkData.push(...editedPoints);
+ }
+ });
+ });
+
+ @undoBatch
syntaxHighlight = action((e: KeyboardEvent | React.PointerEvent | undefined) => {
const selected = this.marqueeSelect(false);
if (e instanceof KeyboardEvent ? e.key === 'i' : true) {