aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/smartdraw/SmartDrawHandler.tsx
diff options
context:
space:
mode:
authoreleanor-park <eleanor_park@brown.edu>2024-08-07 18:09:47 -0400
committereleanor-park <eleanor_park@brown.edu>2024-08-07 18:09:47 -0400
commitfd5278045e8c2e280d81cb965c0b2cc5afb59be8 (patch)
treeea928a5545ffcd584793b0a3b2857747d016d26b /src/client/views/smartdraw/SmartDrawHandler.tsx
parent647b66c965f5896d784de0f321d31cc712937e1c (diff)
problem with setting smooth amount
Diffstat (limited to 'src/client/views/smartdraw/SmartDrawHandler.tsx')
-rw-r--r--src/client/views/smartdraw/SmartDrawHandler.tsx155
1 files changed, 124 insertions, 31 deletions
diff --git a/src/client/views/smartdraw/SmartDrawHandler.tsx b/src/client/views/smartdraw/SmartDrawHandler.tsx
index c842551c3..52df598ee 100644
--- a/src/client/views/smartdraw/SmartDrawHandler.tsx
+++ b/src/client/views/smartdraw/SmartDrawHandler.tsx
@@ -7,17 +7,23 @@ import { ObservableReactComponent } from '../ObservableReactComponent';
import { Button, IconButton } from 'browndash-components';
import ReactLoading from 'react-loading';
import { AiOutlineSend } from 'react-icons/ai';
-import { gptAPICall, GPTCallType } from '../../apis/gpt/GPT';
-import { InkData, InkTool } from '../../../fields/InkField';
+import { gptAPICall, GPTCallType, gptDrawingColor } from '../../apis/gpt/GPT';
+import { InkData, InkField, InkTool } from '../../../fields/InkField';
import { SVGToBezier } from '../../util/bezierFit';
const { parse } = require('svgson');
import { Slider, Switch } from '@mui/material';
-import { Doc } from '../../../fields/Doc';
+import { Doc, DocListCast } from '../../../fields/Doc';
import { DocData } from '../../../fields/DocSymbols';
-import { DocumentView } from '../nodes/DocumentView';
-import { BoolCast, NumCast, StrCast } from '../../../fields/Types';
+import { ActiveArrowEnd, ActiveArrowStart, ActiveDash, ActiveFillColor, ActiveInkBezierApprox, ActiveInkColor, ActiveInkWidth, ActiveIsInkMask, DocumentView } from '../nodes/DocumentView';
+import { BoolCast, ImageCast, NumCast, StrCast } from '../../../fields/Types';
import './SmartDrawHandler.scss';
import { unimplementedFunction } from '../../../Utils';
+import { Docs } from '../../documents/Documents';
+import { MarqueeView } from '../collections/collectionFreeForm';
+import { ImageField, URLField } from '../../../fields/URLField';
+import { CollectionCardView } from '../collections/CollectionCardDeckView';
+import { InkingStroke } from '../InkingStroke';
+import { undoBatch } from '../../util/UndoManager';
export interface DrawingOptions {
text: string;
@@ -46,13 +52,43 @@ export class SmartDrawHandler extends ObservableReactComponent<{}> {
@observable private _autoColor: boolean = true;
@observable private _regenInput: string = '';
@observable private _canInteract: boolean = true;
- public _addFunc: (strokeList: [InkData, string, string][], opts: DrawingOptions, gptRes: string, containerDoc?: Doc) => void = () => {};
- public _deleteFunc: (doc?: Doc) => void = () => {};
+
private _lastInput: DrawingOptions = { text: '', complexity: 5, size: 350, autoColor: true, x: 0, y: 0 };
private _lastResponse: string = '';
private _selectedDoc: Doc | undefined = undefined;
private _errorOccurredOnce = false;
+ public RemoveDrawing: (doc?: Doc) => void = unimplementedFunction;
+ public CreateDrawingDoc: (strokeList: [InkData, string, string][], opts: DrawingOptions, gptRes: string, containerDoc?: Doc) => Doc | undefined = (strokeList: [InkData, string, string][], opts: DrawingOptions, gptRes: string) => {
+ const drawing: Doc[] = [];
+ strokeList.forEach((stroke: [InkData, string, string]) => {
+ const bounds = InkField.getBounds(stroke[0]);
+ const inkWidth = Math.min(5, ActiveInkWidth());
+ const inkDoc = Docs.Create.InkDocument(
+ stroke[0],
+ { title: 'stroke',
+ x: bounds.left - inkWidth / 2,
+ y: bounds.top - inkWidth / 2,
+ _width: bounds.width + inkWidth,
+ _height: bounds.height + inkWidth,
+ stroke_showLabel: BoolCast(Doc.UserDoc().activeInkHideTextLabels)}, // prettier-ignore
+ inkWidth,
+ opts.autoColor ? stroke[1] : ActiveInkColor(),
+ ActiveInkBezierApprox(),
+ stroke[2] === 'none' ? ActiveFillColor() : stroke[2],
+ ActiveArrowStart(),
+ ActiveArrowEnd(),
+ ActiveDash(),
+ ActiveIsInkMask()
+ );
+ drawing.push(inkDoc);
+ });
+
+ const collection = MarqueeView.getCollection(drawing, undefined, true, { left: 1, top: 1, width: 1, height: 1 });
+ return collection;
+ };
+ public AddDrawing: (doc: Doc, opts: DrawingOptions, gptRes: string) => void = unimplementedFunction;
+
constructor(props: any) {
super(props);
makeObservable(this);
@@ -90,25 +126,26 @@ export class SmartDrawHandler extends ObservableReactComponent<{}> {
};
@action
- displaySmartDrawHandler = (x: number, y: number, addFunc: (strokeData: [InkData, string, string][], opts: DrawingOptions, gptRes: string, containerDoc?: Doc) => void, deleteFunc: (doc?: Doc) => void) => {
+ setEdit = () => {
+ this._showEditBox = !this._showEditBox;
+ };
+
+ @action
+ displaySmartDrawHandler = (x: number, y: number) => {
this._pageX = x;
this._pageY = y;
this._display = true;
- this._addFunc = addFunc;
- this._deleteFunc = deleteFunc;
};
@action
- displayRegenerate = (x: number, y: number, addFunc: (strokeData: [InkData, string, string][], opts: DrawingOptions, gptRes: string, containerDoc?: Doc) => void, deleteFunc: (doc?: Doc) => void) => {
+ displayRegenerate = (x: number, y: number) => {
this._selectedDoc = DocumentView.SelectedDocs()?.lastElement();
- const docData = this._selectedDoc[DocData];
- this._addFunc = addFunc;
- this._deleteFunc = deleteFunc;
this._pageX = x;
this._pageY = y;
this._display = false;
this._showRegenerate = true;
this._showEditBox = false;
+ const docData = this._selectedDoc[DocData];
this._lastResponse = StrCast(docData.drawingData);
this._lastInput = { text: StrCast(docData.drawingInput), complexity: NumCast(docData.drawingComplexity), size: NumCast(docData.drawingSize), autoColor: BoolCast(docData.drawingColored), x: this._pageX, y: this._pageY };
};
@@ -155,8 +192,8 @@ export class SmartDrawHandler extends ObservableReactComponent<{}> {
this._showOptions = false;
try {
await this.drawWithGPT({ X: this._pageX, Y: this._pageY }, this._userInput, this._complexity, this._size, this._autoColor);
- this._showRegenerate = true;
this.hideSmartDrawHandler();
+ this._showRegenerate = true;
} catch (err) {
if (this._errorOccurredOnce) {
console.error('GPT call failed', err);
@@ -181,17 +218,15 @@ export class SmartDrawHandler extends ObservableReactComponent<{}> {
return;
}
console.log(res);
- const strokeData = await this.parseResponse(res, startPt, false, autoColor);
+ const strokeData = await this.parseSvg(res, startPt, false, autoColor);
+ const drawingDoc = strokeData && this.CreateDrawingDoc(strokeData?.data, strokeData?.lastInput, strokeData?.lastRes);
+ drawingDoc && this.AddDrawing(drawingDoc, this._lastInput, res);
+
this._errorOccurredOnce = false;
return strokeData;
};
@action
- edit = () => {
- this._showEditBox = !this._showEditBox;
- };
-
- @action
regenerate = async (lastInput?: DrawingOptions, lastResponse?: string, regenInput?: string) => {
if (lastInput) this._lastInput = lastInput;
if (lastResponse) this._lastResponse = lastResponse;
@@ -211,14 +246,18 @@ export class SmartDrawHandler extends ObservableReactComponent<{}> {
return;
}
console.log(res);
- await this.parseResponse(res, { X: this._lastInput.x, Y: this._lastInput.y }, true, lastInput?.autoColor || this._autoColor);
+ const strokeData = await this.parseSvg(res, { X: this._lastInput.x, Y: this._lastInput.y }, true, lastInput?.autoColor || this._autoColor);
+ this.RemoveDrawing !== unimplementedFunction && this.RemoveDrawing(this._selectedDoc);
+ const drawingDoc = strokeData && this.CreateDrawingDoc(strokeData?.data, strokeData?.lastInput, strokeData?.lastRes);
+ drawingDoc && this.AddDrawing(drawingDoc, this._lastInput, res);
+ return strokeData;
} catch (err) {
- console.error('GPT call failed', err);
+ console.error('Error regenerating drawing', err);
}
};
@action
- parseResponse = async (res: string, startPoint: { X: number; Y: number }, regenerate: boolean, autoColor: boolean) => {
+ parseSvg = async (res: string, startPoint: { X: number; Y: number }, regenerate: boolean, autoColor: boolean) => {
const svg = res.match(/<svg[^>]*>([\s\S]*?)<\/svg>/g);
if (svg) {
this._lastResponse = svg[0];
@@ -235,16 +274,70 @@ export class SmartDrawHandler extends ObservableReactComponent<{}> {
(regenerate ? this._lastInput.autoColor : autoColor) ? child.attributes.fill : undefined,
]);
});
- if (regenerate) {
- if (this._deleteFunc !== unimplementedFunction) this._deleteFunc(this._selectedDoc);
- this._addFunc(strokeData, this._lastInput, svg[0]);
- } else {
- this._addFunc(strokeData, this._lastInput, svg[0]);
- }
return { data: strokeData, lastInput: this._lastInput, lastRes: svg[0] };
}
};
+ colorWithGPT = async (drawing: Doc) => {
+ const img = await this.getIcon(drawing);
+ const { href } = (img as URLField).url;
+ const hrefParts = href.split('.');
+ const hrefComplete = `${hrefParts[0]}_o.${hrefParts[1]}`;
+ try {
+ const hrefBase64 = await CollectionCardView.imageUrlToBase64(hrefComplete);
+ const strokes = DocListCast(drawing[DocData].data);
+ const coords: string[] = [];
+ strokes.forEach((stroke, i) => {
+ const inkingStroke = DocumentView.getDocumentView(stroke)?.ComponentView as InkingStroke;
+ const { inkData } = inkingStroke.inkScaledData();
+ coords.push(
+ `${i + 1}. ${inkData
+ .filter((point, index) => {
+ return index % 4 === 0 || index == inkData.length - 1;
+ })
+ .map(point => {
+ return `(${point.X.toString()}, ${point.Y.toString()})`;
+ })}`
+ );
+ });
+ const response = await gptDrawingColor(hrefBase64, coords);
+ console.log(response);
+ const colorResponse = await gptAPICall(response, GPTCallType.COLOR, undefined);
+ console.log(colorResponse);
+ this.colorStrokes(colorResponse, drawing);
+ } catch (error) {
+ console.log('GPT call failed');
+ }
+ };
+
+ @undoBatch
+ colorStrokes = (res: string, drawing: Doc) => {
+ const colorList = res.match(/\{.*?\}/g);
+ const strokes = DocListCast(drawing[DocData].data);
+ colorList?.forEach((colors, index) => {
+ const strokeAndFill = colors.match(/#[0-9A-Fa-f]{6}/g);
+ if (strokeAndFill && strokeAndFill.length == 2) {
+ 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];
+ }
+ }
+ });
+ };
+
+ async getIcon(doc: Doc) {
+ const docView = DocumentView.getDocumentView(doc);
+ console.log(doc);
+ if (docView) {
+ console.log(docView);
+ docView.ComponentView?.updateIcon?.();
+ return new Promise<ImageField | undefined>(res => setTimeout(() => res(ImageCast(docView.Document.icon)), 1000));
+ }
+ return undefined;
+ }
+
render() {
if (this._display) {
return (
@@ -397,7 +490,7 @@ export class SmartDrawHandler extends ObservableReactComponent<{}> {
color={SettingsManager.userColor}
onClick={this.handleSendClick}
/>
- <IconButton tooltip="Edit with GPT" icon={<FontAwesomeIcon icon="pen-to-square" />} color={SettingsManager.userColor} onClick={this.edit} />
+ <IconButton tooltip="Edit with GPT" icon={<FontAwesomeIcon icon="pen-to-square" />} color={SettingsManager.userColor} onClick={this.setEdit} />
{this._showEditBox && (
<div
style={{