aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/client/util/DropConverter.ts4
-rw-r--r--src/client/views/InkStrokeProperties.ts7
-rw-r--r--src/client/views/MarqueeAnnotator.tsx145
-rw-r--r--src/client/views/PropertiesView.scss2
-rw-r--r--src/client/views/PropertiesView.tsx52
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx64
-rw-r--r--src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx2
-rw-r--r--src/client/views/collections/collectionFreeForm/MarqueeView.tsx65
-rw-r--r--src/client/views/pdf/AnchorMenu.tsx20
-rw-r--r--src/client/views/smartdraw/AnnotationPalette.tsx13
-rw-r--r--src/client/views/smartdraw/SmartDrawHandler.tsx22
11 files changed, 100 insertions, 296 deletions
diff --git a/src/client/util/DropConverter.ts b/src/client/util/DropConverter.ts
index 3e26fefad..aa7268b61 100644
--- a/src/client/util/DropConverter.ts
+++ b/src/client/util/DropConverter.ts
@@ -87,6 +87,10 @@ export function makeUserTemplateButton(doc: Doc) {
return dbox;
}
+/**
+ * Similar to makeUserTemplateButton, but rather than creating a draggable button for the template, it takes in
+ * an ImageField that will display.
+ */
export function makeUserTemplateImage(doc: Doc, image: ImageField) {
const layoutDoc = doc; // doc.layout instanceof Doc && doc.layout.isTemplateForField ? doc.layout : doc;
if (layoutDoc.type !== DocumentType.FONTICON) {
diff --git a/src/client/views/InkStrokeProperties.ts b/src/client/views/InkStrokeProperties.ts
index 35d628a4e..ffda126f4 100644
--- a/src/client/views/InkStrokeProperties.ts
+++ b/src/client/views/InkStrokeProperties.ts
@@ -491,6 +491,12 @@ export class InkStrokeProperties {
return inkCopy;
});
+ /**
+ * Function that "smooths" ink strokes by using the gesture recognizer to detect shapes and
+ * removing excess control points with the simplify-js package.
+ * @param inkDocs
+ * @param tolerance Determines how strong the smooth effect will be
+ */
@undoBatch
smoothInkStrokes = (inkDocs: Doc[], tolerance: number = 5) => {
inkDocs.forEach(inkDoc => {
@@ -500,7 +506,6 @@ export class InkStrokeProperties {
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.92 : result.Score > 0.85)) {
switch (result.Name) {
case Gestures.Line:
diff --git a/src/client/views/MarqueeAnnotator.tsx b/src/client/views/MarqueeAnnotator.tsx
index f06f3efe0..3d0216625 100644
--- a/src/client/views/MarqueeAnnotator.tsx
+++ b/src/client/views/MarqueeAnnotator.tsx
@@ -28,7 +28,6 @@ export interface MarqueeAnnotatorProps {
marqueeContainer: HTMLDivElement;
docView: () => DocumentView;
savedAnnotations: () => ObservableMap<number, HTMLDivElement[]>;
- savedTapes: () => ObservableMap<number, HTMLDivElement[]>;
selectionText: () => string;
annotationLayer: HTMLDivElement;
addDocument: (doc: Doc) => boolean;
@@ -75,7 +74,6 @@ export class MarqueeAnnotator extends ObservableReactComponent<MarqueeAnnotatorP
backgroundColor: color,
annotationOn: this.props.Document,
title: 'Annotation on ' + this.props.Document.title,
- a,
});
marqueeAnno.x = NumCast(doc.freeform_panX_min) + (parseInt(anno.style.left || '0') - containerOffset[0]) / scale;
marqueeAnno.y = NumCast(doc.freeform_panY_min) + (parseInt(anno.style.top || '0') - containerOffset[1]) / scale;
@@ -130,139 +128,6 @@ export class MarqueeAnnotator extends ObservableReactComponent<MarqueeAnnotatorP
return textRegionAnno;
};
- // @undoBatch
- // makeTapeDocument = (color: string, isLinkButton?: boolean, savedTapes?: ObservableMap<number, HTMLDivElement[]>): Opt<Doc> => {
- // const savedTapeMap = savedTapes?.values() && Array.from(savedTapes?.values()).length ? savedTapes : this.props.savedTapes();
- // if (savedTapeMap.size === 0) return undefined;
- // const tapes = Array.from(savedTapeMap.values())[0];
- // const doc = this.props.Document;
- // const scale = (this.props.annotationLayerScaling?.() || 1) * NumCast(doc._freeform_scale, 1);
- // if (tapes.length && (tapes[0] as any).marqueeing) {
- // const anno = tapes[0];
- // const containerOffset = this.props.containerOffset?.() || [0, 0];
- // const tape = Docs.Create.FreeformDocument([], {
- // onClick: isLinkButton ? FollowLinkScript() : undefined,
- // backgroundColor: color,
- // annotationOn: this.props.Document,
- // title: 'Tape on ' + this.props.Document.title,
- // });
- // tape.x = NumCast(doc.freeform_panX_min) + (parseInt(anno.style.left || '0') - containerOffset[0]) / scale;
- // tape.y = NumCast(doc.freeform_panY_min) + (parseInt(anno.style.top || '0') - containerOffset[1]) / scale;
- // tape._height = parseInt(anno.style.height || '0') / scale;
- // tape._width = parseInt(anno.style.width || '0') / scale;
- // anno.remove();
- // savedTapeMap.clear();
- // return tape;
- // }
-
- // const textRegionAnno = Docs.Create.ConfigDocument({
- // annotationOn: this.props.Document,
- // text: this.props.selectionText() as any, // text want an RTFfield, but strings are acceptable, too.
- // text_html: this.props.selectionText() as any,
- // backgroundColor: 'transparent',
- // presentation_duration: 2100,
- // presentation_transition: 500,
- // presentation_zoomText: true,
- // title: '>' + this.props.Document.title,
- // });
- // const textRegionAnnoProto = textRegionAnno[DocData];
- // let minX = Number.MAX_VALUE;
- // let maxX = -Number.MAX_VALUE;
- // let minY = Number.MAX_VALUE;
- // let maxY = -Number.MIN_VALUE;
- // const annoRects: string[] = [];
- // savedAnnoMap.forEach((value: HTMLDivElement[]) =>
- // value.forEach(anno => {
- // const x = parseInt(anno.style.left ?? '0');
- // const y = parseInt(anno.style.top ?? '0');
- // const height = parseInt(anno.style.height ?? '0');
- // const width = parseInt(anno.style.width ?? '0');
- // annoRects.push(`${x}:${y}:${width}:${height}`);
- // anno.remove();
- // minY = Math.min(NumCast(y), minY);
- // minX = Math.min(NumCast(x), minX);
- // maxY = Math.max(NumCast(y) + NumCast(height), maxY);
- // maxX = Math.max(NumCast(x) + NumCast(width), maxX);
- // })
- // );
-
- // textRegionAnnoProto.y = Math.max(minY, 0);
- // textRegionAnnoProto.x = Math.max(minX, 0);
- // textRegionAnnoProto.height = Math.max(maxY, 0) - Math.max(minY, 0);
- // textRegionAnnoProto.width = Math.max(maxX, 0) - Math.max(minX, 0);
- // textRegionAnnoProto.backgroundColor = color;
- // // mainAnnoDocProto.text = this._selectionText;
- // textRegionAnnoProto.text_inlineAnnotations = new List<string>(annoRects);
- // textRegionAnnoProto.opacity = 0;
- // textRegionAnnoProto.layout_unrendered = true;
- // savedAnnoMap.clear();
- // return textRegionAnno;
- // };
-
- @undoBatch
- makeTapeDocument = (color: string, isLinkButton?: boolean, savedTapes?: ObservableMap<number, HTMLDivElement[]>): Opt<Doc> => {
- // const savedAnnoMap = savedTapes?.values() && Array.from(savedTapes?.values()).length ? savedTapes : this.props.savedTapes();
- // if (savedAnnoMap.size === 0) return undefined;
- // const savedAnnos = Array.from(savedAnnoMap.values())[0];
- const doc = this.props.Document;
- const scale = (this.props.annotationLayerScaling?.() || 1) * NumCast(doc._freeform_scale, 1);
- const marqueeAnno = Docs.Create.FreeformDocument([], {
- onClick: isLinkButton ? FollowLinkScript() : undefined,
- backgroundColor: color,
- annotationOn: this.props.Document,
- title: 'Annotation on ' + this.props.Document.title,
- });
- marqueeAnno.x = NumCast(doc.freeform_panX_min) / scale;
- marqueeAnno.y = NumCast(doc.freeform_panY_min) / scale;
- marqueeAnno._height = parseInt('100') / scale;
- marqueeAnno._width = parseInt('100') / scale;
- return marqueeAnno;
- // }
-
- // const textRegionAnno = Docs.Create.ConfigDocument({
- // annotationOn: this.props.Document,
- // text: this.props.selectionText() as any, // text want an RTFfield, but strings are acceptable, too.
- // text_html: this.props.selectionText() as any,
- // backgroundColor: 'transparent',
- // presentation_duration: 2100,
- // presentation_transition: 500,
- // presentation_zoomText: true,
- // title: '>' + this.props.Document.title,
- // });
- // const textRegionAnnoProto = textRegionAnno[DocData];
- // let minX = Number.MAX_VALUE;
- // let maxX = -Number.MAX_VALUE;
- // let minY = Number.MAX_VALUE;
- // let maxY = -Number.MIN_VALUE;
- // const annoRects: string[] = [];
- // savedAnnoMap.forEach((value: HTMLDivElement[]) =>
- // value.forEach(anno => {
- // const x = parseInt(anno.style.left ?? '0');
- // const y = parseInt(anno.style.top ?? '0');
- // const height = parseInt(anno.style.height ?? '0');
- // const width = parseInt(anno.style.width ?? '0');
- // annoRects.push(`${x}:${y}:${width}:${height}`);
- // anno.remove();
- // minY = Math.min(NumCast(y), minY);
- // minX = Math.min(NumCast(x), minX);
- // maxY = Math.max(NumCast(y) + NumCast(height), maxY);
- // maxX = Math.max(NumCast(x) + NumCast(width), maxX);
- // })
- // );
-
- // textRegionAnnoProto.y = Math.max(minY, 0);
- // textRegionAnnoProto.x = Math.max(minX, 0);
- // textRegionAnnoProto.height = Math.max(maxY, 0) - Math.max(minY, 0);
- // textRegionAnnoProto.width = Math.max(maxX, 0) - Math.max(minX, 0);
- // textRegionAnnoProto.backgroundColor = color;
- // // mainAnnoDocProto.text = this._selectionText;
- // textRegionAnnoProto.text_inlineAnnotations = new List<string>(annoRects);
- // textRegionAnnoProto.opacity = 0;
- // textRegionAnnoProto.layout_unrendered = true;
- // savedAnnoMap.clear();
- // return textRegionAnno;
- };
-
@action
highlight = (color: string, isLinkButton: boolean, savedAnnotations?: ObservableMap<number, HTMLDivElement[]>, addAsAnnotation?: boolean) => {
// creates annotation documents for current highlights
@@ -272,15 +137,6 @@ export class MarqueeAnnotator extends ObservableReactComponent<MarqueeAnnotatorP
return annotationDoc as Doc;
};
- @action
- tape = (color: string, isLinkButton: boolean, savedTapes?: ObservableMap<number, HTMLDivElement[]>, addAsAnnotation?: boolean) => {
- // creates annotation documents for current highlights
- const effectiveAcl = GetEffectiveAcl(this.props.Document[DocData]);
- const tape = [AclAugment, AclSelfEdit, AclEdit, AclAdmin].includes(effectiveAcl) && this.makeTapeDocument(color, isLinkButton, savedTapes);
- addAsAnnotation && tape && this.props.addDocument(tape);
- return tape as Doc;
- };
-
public static previewNewAnnotation = action((savedAnnotations: ObservableMap<number, HTMLDivElement[]>, annotationLayer: HTMLDivElement, div: HTMLDivElement, page: number) => {
div.style.backgroundColor = '#ACCEF7';
div.style.opacity = '0.5';
@@ -327,7 +183,6 @@ export class MarqueeAnnotator extends ObservableReactComponent<MarqueeAnnotatorP
AnchorMenu.Instance.OnClick = undoable(() => this.props.anchorMenuClick?.()?.(this.highlight(this.props.highlightDragSrcColor ?? 'rgba(173, 216, 230, 0.75)', true, undefined, true)), 'make sidebar annotation');
AnchorMenu.Instance.OnAudio = unimplementedFunction;
AnchorMenu.Instance.Highlight = (color: string) => this.highlight(color, false, undefined, true);
- AnchorMenu.Instance.Tape = (color: string) => this.tape(color, false, undefined, true);
AnchorMenu.Instance.GetAnchor = (savedAnnotations?: ObservableMap<number, HTMLDivElement[]> /* , addAsAnnotation?: boolean */) => this.highlight('rgba(173, 216, 230, 0.75)', true, savedAnnotations, true);
AnchorMenu.Instance.onMakeAnchor = () => AnchorMenu.Instance.GetAnchor(undefined, true);
diff --git a/src/client/views/PropertiesView.scss b/src/client/views/PropertiesView.scss
index aa825a6e9..a5e60b831 100644
--- a/src/client/views/PropertiesView.scss
+++ b/src/client/views/PropertiesView.scss
@@ -642,5 +642,5 @@
.smooth,
.color,
.smooth-slider {
- margin-top: 3px;
+ margin-top: 7px;
}
diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx
index ac2625f32..c7b0a2ba5 100644
--- a/src/client/views/PropertiesView.tsx
+++ b/src/client/views/PropertiesView.tsx
@@ -802,7 +802,7 @@ export class PropertiesView extends ObservableReactComponent<PropertiesViewProps
};
getField(key: string) {
- return this.containsInkDoc ? Field.toString(this.inkDoc?.[DocData][key] as FieldType) : Field.toString(this.selectedDoc?.[DocData][key] as FieldType);
+ return Field.toString(this.selectedDoc?.[DocData][key] as FieldType);
}
@computed get shapeXps() { return NumCast(this.selectedDoc?.x); } // prettier-ignore
@@ -960,6 +960,22 @@ export class PropertiesView extends ObservableReactComponent<PropertiesViewProps
const targetDoc = this.selectedLayoutDoc;
return (
<div>
+ {!targetDoc.layout_isSvg && (
+ <div className="color">
+ <Toggle
+ text={'Color with GPT'}
+ color={SettingsManager.userColor}
+ icon={<FontAwesomeIcon icon="fill-drip" />}
+ iconPlacement="left"
+ align="flex-start"
+ fillWidth
+ toggleType={ToggleType.BUTTON}
+ onClick={undoable(() => {
+ SmartDrawHandler.Instance.colorWithGPT(targetDoc);
+ }, 'smoothStrokes')}
+ />
+ </div>
+ )}
<div className="smooth">
<Toggle
text={'Smooth Ink Strokes'}
@@ -988,22 +1004,6 @@ export class PropertiesView extends ObservableReactComponent<PropertiesViewProps
1
)}
</div>
- {!targetDoc.layout_isSvg && (
- <div className="color">
- <Toggle
- text={'Color with GPT'}
- color={SettingsManager.userColor}
- icon={<FontAwesomeIcon icon="fill-drip" />}
- iconPlacement="left"
- align="flex-start"
- fillWidth
- toggleType={ToggleType.BUTTON}
- onClick={undoable(() => {
- SmartDrawHandler.Instance.colorWithGPT(targetDoc);
- }, 'smoothStrokes')}
- />
- </div>
- )}
</div>
);
}
@@ -1026,9 +1026,15 @@ export class PropertiesView extends ObservableReactComponent<PropertiesViewProps
}
@computed get markScal() { return Number(this.getField('stroke_markerScale') || '1'); } // prettier-ignore
set markScal(value) {
+ if (this.containsInkDoc) {
+ const childDocs = DocListCast(this.selectedDoc[DocData].data);
+ childDocs.forEach(doc => {
+ doc[DocData].stroke_markerScale = Number(value);
+ });
+ }
this.selectedDoc && (this.selectedDoc[DocData].stroke_markerScale = Number(value));
}
- @computed get smoothAmt() { return Number(this.getField('stroke_smoothAmount') || '1'); } // prettier-ignore
+ @computed get smoothAmt() { return Number(this.getField('stroke_smoothAmount') || '10'); } // prettier-ignore
set smoothAmt(value) {
this.selectedDoc && (this.selectedDoc[DocData].stroke_smoothAmount = Number(value));
}
@@ -1039,9 +1045,8 @@ export class PropertiesView extends ObservableReactComponent<PropertiesViewProps
childDocs.forEach(doc => {
doc[DocData].stroke_startMarker = value;
});
- } else {
- this.selectedDoc && (this.selectedDoc[DocData].stroke_startMarker = value);
}
+ this.selectedDoc && (this.selectedDoc[DocData].stroke_startMarker = value);
}
@computed get markTail() { return this.getField('stroke_endMarker') || ''; } // prettier-ignore
set markTail(value) {
@@ -1050,9 +1055,8 @@ export class PropertiesView extends ObservableReactComponent<PropertiesViewProps
childDocs.forEach(doc => {
doc[DocData].stroke_endMarker = value;
});
- } else {
- this.selectedDoc && (this.selectedDoc[DocData].stroke_endMarker = value);
}
+ this.selectedDoc && (this.selectedDoc[DocData].stroke_endMarker = value);
}
regInput = (key: string, value: any, setter: (val: string) => {}) => (
@@ -1304,6 +1308,10 @@ export class PropertiesView extends ObservableReactComponent<PropertiesViewProps
);
}
+ /**
+ * Determines if a selected collection/group document contains any ink strokes to allow users to edit groups
+ * of ink strokes in the properties menu.
+ */
containsInk = (selectedDoc: Doc) => {
const childDocs: Doc[] = DocListCast(selectedDoc[DocData].data);
for (var i = 0; i < childDocs.length; i++) {
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 8575807b3..8dc5f03b0 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -599,6 +599,15 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
_eraserLock = 0;
_eraserPts: number[][] = []; // keep track of the last few eraserPts to make the eraser circle 'stretch'
+ /**
+ * Erases strokes by intersecting them with an invisible "eraser stroke".
+ * By default this iterates through all intersected ink strokes, determines which parts of a stroke need to be erased based on the type
+ * of eraser, draws back the ink segments to keep, and deletes the original stroke.
+ *
+ * Radius eraser: erases strokes by intersecting them with a circle of variable radius. Essentially creates an InkField for the
+ * eraser circle, then determines its intersections with other ink strokes. Each stroke's DocumentView and its
+ * intersection t-values are put into a map, which gets looped through to take out the erased parts.
+ */
erase = (e: PointerEvent, delta: number[]) => {
e.stopImmediatePropagation();
const currPoint = { X: e.clientX, Y: e.clientY };
@@ -646,11 +655,6 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
return false;
};
- /**
- * Erases strokes by intersecting them with an invisible "eraser stroke".
- * By default this iterates through all intersected ink strokes, determines their segmentation, draws back the non-intersected segments,
- * and deletes the original stroke.
- */
@action
onEraserMove = (e: PointerEvent, down: number[], delta: number[]) => {
this.erase(e, delta);
@@ -665,42 +669,6 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
this.erase(e, [0, 0]);
};
- /**
- * Erases strokes by intersecting them with a circle of variable radius. Essentially creates an InkField for the
- * eraser circle, then determines its intersections with other ink strokes. Each stroke's DocumentView and its
- * intersection t-values are put into a map, which gets looped through to take out the erased parts.
- * @param e
- * @param down
- * @param delta
- * @returns
- */
- // @action
- // onRadiusEraserMove = (e: PointerEvent, down: number[], delta: number[]) => {
- // const currPoint = { X: e.clientX, Y: e.clientY };
- // this._eraserPts.push([currPoint.X, currPoint.Y]);
- // this._eraserPts = this._eraserPts.slice(Math.max(0, this._eraserPts.length - 5));
- // const strokeMap: Map<DocumentView, number[]> = this.getRadiusEraserIntersections({ X: currPoint.X - delta[0], Y: currPoint.Y - delta[1] }, currPoint);
-
- // strokeMap.forEach((intersects, stroke) => {
- // if (!this._deleteList.includes(stroke)) {
- // this._deleteList.push(stroke);
- // SetActiveInkWidth(StrCast(stroke.Document.stroke_width?.toString()) || '1');
- // SetActiveInkColor(StrCast(stroke.Document.color?.toString()) || 'black');
- // const segments = this.radiusErase(stroke, intersects.sort());
- // segments?.forEach(segment =>
- // this.forceStrokeGesture(
- // e,
- // Gestures.Stroke,
- // segment.reduce((data, curve) => [...data, ...curve.points.map(p => stroke.ComponentView?.ptToScreen?.({ X: p.x, Y: p.y }) ?? { X: 0, Y: 0 })], [] as PointData[])
- // )
- // );
- // }
- // stroke.layoutDoc.opacity = 0;
- // stroke.layoutDoc.dontIntersect = true;
- // });
- // return false;
- // };
-
forceStrokeGesture = (e: PointerEvent, gesture: Gestures, points: InkData, text?: any) => {
this.onGesture(e, new GestureUtils.GestureEvent(gesture, points, InkField.getBounds(points), text));
};
@@ -1240,6 +1208,9 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
return tVals;
};
+ /**
+ * Creates an ink document to add to the freeform canvas.
+ */
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);
@@ -1273,6 +1244,9 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
_drawing: Doc[] = [];
_drawingContainer: Doc | undefined = undefined;
+ /**
+ * Function that creates a drawing--a group of ink strokes--to go with the smart draw function.
+ */
@undoBatch
createDrawingDoc = (strokeData: [InkData, string, string][], opts: DrawingOptions, gptRes: string) => {
this._drawing = [];
@@ -1304,6 +1278,9 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
return collection;
};
+ /**
+ * Part of regenerating a drawing--deletes the old drawing.
+ */
removeDrawing = (doc?: Doc) => {
this._batch = UndoManager.StartBatch('regenerateDrawing');
if (doc) {
@@ -1317,6 +1294,9 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
this._drawing = [];
};
+ /**
+ * Adds the created drawing to the freeform canvas and sets the metadata.
+ */
addDrawing = (doc: Doc, opts: DrawingOptions, gptRes: string) => {
const docData = doc[DocData];
docData.title = opts.text.match(/^(.*?)~~~.*$/)?.[1] || opts.text;
@@ -1926,8 +1906,6 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
const locPt = this.ScreenToLocalBoxXf().transformPoint(e.clientX, e.clientY);
this._eraserX = locPt[0];
this._eraserY = locPt[1];
- // Doc.ActiveTool === InkTool.RadiusEraser ? this._childPointerEvents = 'none' : this._childPointerEvents = 'all'
- // super.setCursorPosition(this.getTransform().transformPoint(e.clientX, e.clientY));
};
@action
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx b/src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx
index 76c37dff0..b3fdd9379 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx
@@ -21,7 +21,6 @@ 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);
@@ -42,7 +41,6 @@ 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 92c0da983..bc1dfed92 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -284,7 +284,6 @@ 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 {
@@ -498,70 +497,6 @@ 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) {
diff --git a/src/client/views/pdf/AnchorMenu.tsx b/src/client/views/pdf/AnchorMenu.tsx
index ea574493a..7719f2f7c 100644
--- a/src/client/views/pdf/AnchorMenu.tsx
+++ b/src/client/views/pdf/AnchorMenu.tsx
@@ -57,7 +57,6 @@ export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
public StartDrag: (e: PointerEvent, ele: HTMLElement) => void = unimplementedFunction;
public StartCropDrag: (e: PointerEvent, ele: HTMLElement) => void = unimplementedFunction;
public Highlight: (color: string) => Opt<Doc> = (/* color: string */) => undefined;
- public Tape: (color: string) => Opt<Doc> = (/* color: string */) => undefined;
public GetAnchor: (savedAnnotations: Opt<ObservableMap<number, HTMLDivElement[]>>, addAsAnnotation: boolean) => Opt<Doc> = emptyFunction;
public Delete: () => void = unimplementedFunction;
public PinToPres: () => void = unimplementedFunction;
@@ -144,6 +143,9 @@ export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
this.addToCollection?.(newCol);
};
+ /**
+ * Creates a GPT drawing based on selected text.
+ */
gptDraw = async (e: React.PointerEvent) => {
try {
SmartDrawHandler.Instance.AddDrawing = this.createDrawingAnnotation;
@@ -155,6 +157,9 @@ export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
}
};
+ /**
+ * Defines how a GPT drawing should be added to the current document.
+ */
@undoBatch
createDrawingAnnotation = action((drawing: Doc, opts: DrawingOptions, gptRes: string) => {
this.AddDrawingAnnotation(drawing);
@@ -203,12 +208,6 @@ export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
AnchorMenu.Instance.fadeOut(true);
};
- @action
- tapeClicked = () => {
- this.Tape(this.highlightColor);
- // AnchorMenu.Instance.fadeOut(true);
- };
-
@computed get highlighter() {
return (
<Group>
@@ -219,13 +218,6 @@ export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
colorPicker={this.highlightColor}
color={SettingsManager.userColor}
/>
- <IconButton
- tooltip="Click to Add Tape" //
- icon={<FontAwesomeIcon icon="tape" />}
- onClick={this.tapeClicked}
- colorPicker={this.highlightColor}
- color={SettingsManager.userColor}
- />
<ColorPicker selectedColor={this.highlightColor} setFinalColor={this.changeHighlightColor} setSelectedColor={this.changeHighlightColor} size={Size.XSMALL} />
</Group>
);
diff --git a/src/client/views/smartdraw/AnnotationPalette.tsx b/src/client/views/smartdraw/AnnotationPalette.tsx
index 7e4d46204..7f00fa2f2 100644
--- a/src/client/views/smartdraw/AnnotationPalette.tsx
+++ b/src/client/views/smartdraw/AnnotationPalette.tsx
@@ -107,6 +107,10 @@ export class AnnotationPalette extends ObservableReactComponent<AnnotationPalett
this._props.Document[DocData].data = undefined;
};
+ /**
+ * Adds a doc to the annotation palette. Gets a snapshot of the document to use as a preview in the palette. When this
+ * preview is dragged onto a parent document, a copy of that document is added as an annotation.
+ */
public static addToPalette = async (doc: Doc) => {
if (!doc.savedAsAnno) {
const clone = await Doc.MakeClone(doc);
@@ -129,6 +133,10 @@ export class AnnotationPalette extends ObservableReactComponent<AnnotationPalett
return undefined;
}
+ /**
+ * Calls the draw with GPT functions in SmartDrawHandler to allow users to generate drawings straight from
+ * the annotation palette.
+ */
@undoBatch
generateDrawings = action(async () => {
this._isLoading = true;
@@ -159,6 +167,11 @@ export class AnnotationPalette extends ObservableReactComponent<AnnotationPalett
Doc.AddDocToList(this._props.Document, 'data', drawing);
};
+ /**
+ * Saves the currently showing, newly generated drawing to the annotation palette and sets the metadata.
+ * AddToPalette() is generically used to add any document to the palette, while this defines the behavior for when a user
+ * presses the "save drawing" button.
+ */
saveDrawing = async () => {
const cIndex: number = this._props.Document.carousel_index as number;
const focusedDrawing = DocListCast(this._props.Document.data)[cIndex];
diff --git a/src/client/views/smartdraw/SmartDrawHandler.tsx b/src/client/views/smartdraw/SmartDrawHandler.tsx
index 52df598ee..4ec787e07 100644
--- a/src/client/views/smartdraw/SmartDrawHandler.tsx
+++ b/src/client/views/smartdraw/SmartDrawHandler.tsx
@@ -208,6 +208,9 @@ export class SmartDrawHandler extends ObservableReactComponent<{}> {
this._canInteract = true;
};
+ /**
+ * Calls GPT API to create a drawing based on user input
+ */
@action
drawWithGPT = async (startPt: { X: number; Y: number }, input: string, complexity: number, size: number, autoColor: boolean) => {
if (input === '') return;
@@ -226,6 +229,9 @@ export class SmartDrawHandler extends ObservableReactComponent<{}> {
return strokeData;
};
+ /**
+ * Regenerates drawings with the option to add a specific regenerate prompt/request.
+ */
@action
regenerate = async (lastInput?: DrawingOptions, lastResponse?: string, regenInput?: string) => {
if (lastInput) this._lastInput = lastInput;
@@ -256,6 +262,9 @@ export class SmartDrawHandler extends ObservableReactComponent<{}> {
}
};
+ /**
+ * Parses the svg code that GPT returns into Bezier curves.
+ */
@action
parseSvg = async (res: string, startPoint: { X: number; Y: number }, regenerate: boolean, autoColor: boolean) => {
const svg = res.match(/<svg[^>]*>([\s\S]*?)<\/svg>/g);
@@ -278,6 +287,9 @@ export class SmartDrawHandler extends ObservableReactComponent<{}> {
}
};
+ /**
+ * Sends request to GPT API to recolor a selected ink document or group of ink documents.
+ */
colorWithGPT = async (drawing: Doc) => {
const img = await this.getIcon(drawing);
const { href } = (img as URLField).url;
@@ -310,6 +322,9 @@ export class SmartDrawHandler extends ObservableReactComponent<{}> {
}
};
+ /**
+ * Function that parses the GPT color response and sets the selected stroke(s) to the new color.
+ */
@undoBatch
colorStrokes = (res: string, drawing: Doc) => {
const colorList = res.match(/\{.*?\}/g);
@@ -320,13 +335,14 @@ export class SmartDrawHandler extends ObservableReactComponent<{}> {
strokes[index][DocData].color = strokeAndFill[0];
const inkStroke = DocumentView.getDocumentView(strokes[index])?.ComponentView as InkingStroke;
const { inkData } = inkStroke.inkScaledData();
- if (InkingStroke.IsClosed(inkData)) {
- strokes[index][DocData].fillColor = strokeAndFill[1];
- }
+ InkingStroke.IsClosed(inkData) ? (strokes[index][DocData].fillColor = strokeAndFill[1]) : (strokes[index][DocData].fillColor = undefined);
}
});
};
+ /**
+ * Gets an image snapshot of a doc. In this class, it's used to snapshot a selected ink stroke/group to use for GPT color.
+ */
async getIcon(doc: Doc) {
const docView = DocumentView.getDocumentView(doc);
console.log(doc);