diff options
author | eleanor-park <eleanor_park@brown.edu> | 2024-08-07 18:09:47 -0400 |
---|---|---|
committer | eleanor-park <eleanor_park@brown.edu> | 2024-08-07 18:09:47 -0400 |
commit | fd5278045e8c2e280d81cb965c0b2cc5afb59be8 (patch) | |
tree | ea928a5545ffcd584793b0a3b2857747d016d26b /src/client/views/smartdraw/SmartDrawHandler.tsx | |
parent | 647b66c965f5896d784de0f321d31cc712937e1c (diff) |
problem with setting smooth amount
Diffstat (limited to 'src/client/views/smartdraw/SmartDrawHandler.tsx')
-rw-r--r-- | src/client/views/smartdraw/SmartDrawHandler.tsx | 155 |
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={{ |