From 3d14f06ad1297a6adc851945804211efb9dff7ff Mon Sep 17 00:00:00 2001 From: eleanor-park Date: Thu, 20 Jun 2024 16:42:44 -0400 Subject: user customization added --- .../collectionFreeForm/CollectionFreeFormView.tsx | 14 +- .../collectionFreeForm/ImageLabelHandler.scss | 14 ++ .../collectionFreeForm/SmartDrawHandler.tsx | 261 +++++++++++++++------ 3 files changed, 207 insertions(+), 82 deletions(-) (limited to 'src/client/views/collections/collectionFreeForm') diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 93b63ac4c..b8257ff31 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -1268,23 +1268,25 @@ export class CollectionFreeFormView extends CollectionSubView { - console.log(strokeList.length); - strokeList.forEach(inkData => { + createInkStrokes = (strokeData: [InkData, string, string][]) => { + strokeData.forEach((stroke: [InkData, string, string]) => { // const points: InkData = FitCurve(inkData, 20) as InkData; // const allPts = GenerateControlPoints(inkData, alpha); - const bounds = InkField.getBounds(inkData); + const bounds = InkField.getBounds(stroke[0]); const B = this.screenToFreeformContentsXf.transformBounds(bounds.left, bounds.top, bounds.width, bounds.height); const inkWidth = ActiveInkWidth() * this.ScreenToLocalBoxXf().Scale; const inkDoc = Docs.Create.InkDocument( - inkData, + stroke[0], { 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 + inkWidth, + stroke[1], + undefined, + stroke[2] === 'none' ? undefined : stroke[2] ); this.addDocument(inkDoc); }); diff --git a/src/client/views/collections/collectionFreeForm/ImageLabelHandler.scss b/src/client/views/collections/collectionFreeForm/ImageLabelHandler.scss index e7413bf8e..9b8727e1a 100644 --- a/src/client/views/collections/collectionFreeForm/ImageLabelHandler.scss +++ b/src/client/views/collections/collectionFreeForm/ImageLabelHandler.scss @@ -42,3 +42,17 @@ } } } + +.complexity-slider { + width: 50%; /* Full-width */ + height: 25px; /* Specified height */ + background: #d3d3d3; /* Grey background */ + outline: none; /* Remove outline */ + opacity: 0.7; /* Set transparency (for mouse-over effects on hover) */ + -webkit-transition: 0.2s; /* 0.2 seconds transition on hover */ + transition: opacity 0.2s; + + :hover { + opacity: 1; /* Fully shown on mouse-over */ + } +} diff --git a/src/client/views/collections/collectionFreeForm/SmartDrawHandler.tsx b/src/client/views/collections/collectionFreeForm/SmartDrawHandler.tsx index 956a8d7e9..edb814172 100644 --- a/src/client/views/collections/collectionFreeForm/SmartDrawHandler.tsx +++ b/src/client/views/collections/collectionFreeForm/SmartDrawHandler.tsx @@ -4,16 +4,20 @@ import { observer } from 'mobx-react'; import React from 'react'; import { SettingsManager } from '../../../util/SettingsManager'; import { ObservableReactComponent } from '../../ObservableReactComponent'; -import { Button, IconButton } from 'browndash-components'; +import { Button, IconButton, Size } from 'browndash-components'; import ReactLoading from 'react-loading'; import { AiOutlineSend } from 'react-icons/ai'; -import { MarqueeOptionsMenu } from './MarqueeOptionsMenu'; import './ImageLabelHandler.scss'; import { gptAPICall, GPTCallType } from '../../../apis/gpt/GPT'; import { InkData } from '../../../../fields/InkField'; -import { ButtonType } from '../../nodes/FontIconBox/FontIconBox'; import { SVGToBezier } from '../../../util/bezierFit'; -const { parse, stringify } = require('svgson'); +const { parse } = require('svgson'); +import { Slider, Switch } from '@mui/material'; +import { IconProp } from '@fortawesome/fontawesome-svg-core'; +import { Flex } from '@adobe/react-spectrum'; +import { Row } from 'react-aria-components'; +import { UndoManager } from '../../../util/UndoManager'; +import e from 'cors'; @observer export class SmartDrawHandler extends ObservableReactComponent<{}> { @@ -25,11 +29,15 @@ export class SmartDrawHandler extends ObservableReactComponent<{}> { @observable private _yRelativeToTop: boolean = true; @observable private _isLoading: boolean = false; @observable private _userInput: string = ''; - @observable private _drawingTypeToolTip = 'Create Geometric Drawing'; - @observable private _drawingTypeIcon: 'star' | 'splotch' = 'star'; - @observable private _alpha: number | undefined = undefined; // number between 0 and 1 that determines how rounded a drawing will be - // @observable public strokes: InkData[] = []; - private _addToDocFunc: (strokeList: InkData[], alpha?: number) => void = () => {}; + @observable private _showOptions: boolean = false; + @observable private _menuIcon: string = 'caret-right'; + @observable private _complexity: number = 5; + @observable private _size: number = 300; + @observable private _autoColor: boolean = true; + @observable private _showRegenerate: boolean = false; + private _addToDocFunc: (strokeList: [InkData, string, string][]) => void = () => {}; + private _lastX: number = 0; + private _lastY: number = 0; constructor(props: any) { super(props); @@ -37,97 +45,82 @@ export class SmartDrawHandler extends ObservableReactComponent<{}> { SmartDrawHandler.Instance = this; } - @action - setIsLoading = (isLoading: boolean) => { - this._isLoading = isLoading; - }; - @action setUserInput = (input: string) => { this._userInput = input; }; @action - displaySmartDrawHandler = (x: number, y: number, addToDoc: (strokeList: InkData[], alpha?: number) => void) => { + displaySmartDrawHandler = (x: number, y: number, addToDoc: (strokeData: [InkData, string, string][]) => void) => { this._pageX = x; this._pageY = y; this._display = true; this._addToDocFunc = addToDoc; }; - @action hideSmartDrawHandler = () => { + this._showRegenerate = false; this._display = false; + this._isLoading = false; + this._showOptions = false; + this._menuIcon = 'caret-right'; + }; + + hideRegenerate = () => { + this._showRegenerate = false; + this._userInput = ''; + this._complexity = 5; + this._size = 300; + this._autoColor = true; + this._isLoading = false; + }; + + toggleMenu = () => { + this._showOptions = !this._showOptions; + this._menuIcon === 'caret-right' ? (this._menuIcon = 'caret-down') : (this._menuIcon = 'caret-right'); }; @action - drawWithGPT = async (startPoint: { X: number; Y: number }, input: string) => { - console.log('start point is', startPoint); - this.setIsLoading(true); + drawWithGPT = async (e: React.MouseEvent, startPoint: { X: number; Y: number }, input: string, regenerate: boolean = false) => { + if (this._userInput === '') return; + e.stopPropagation(); + this._lastX = startPoint.X; + this._lastY = startPoint.Y; + this._isLoading = true; + this._showOptions = false; try { - const res = await gptAPICall(input, GPTCallType.DRAW); + const res = await gptAPICall(`"${input}", "${this._complexity}", "${this._size}"`, GPTCallType.DRAW); if (!res) { console.error('GPT call failed'); return; } - console.log('GPT response:', res); const svg = res.match(/]*>([\s\S]*?)<\/svg>/g); - console.log('svg', svg); if (svg) { const svgObject = await parse(svg[0]); - console.log('svg object', svgObject); const svgStrokes: any = svgObject.children; - const beziers: InkData[] = []; - svgStrokes.forEach((stroke: any) => { - const convertedBezier: InkData = SVGToBezier(stroke.name, stroke.attributes); - beziers.push( + const strokeData: [InkData, string, string][] = []; + svgStrokes.forEach((child: any) => { + const convertedBezier: InkData = SVGToBezier(child.name, child.attributes); + strokeData.push([ convertedBezier.map(point => { - return { X: point.X + startPoint.X, Y: point.Y + startPoint.Y }; - }) - ); + return { X: point.X + startPoint.X - this._size / 1.5, Y: point.Y + startPoint.Y - this._size / 2 }; + }), + this._autoColor ? child.attributes.stroke : undefined, + this._autoColor ? child.attributes.fill : undefined, + ]); }); - this._addToDocFunc(beziers); + if (regenerate) UndoManager.Undo(); + this._addToDocFunc(strokeData); } - - // const strokes = res.trim().split(/\s*(?=\s*M)/); // prettier-ignore - // const parsedSegments: InkData[] = []; - // console.log('strokes', strokes); - // strokes.forEach(stroke => { - // stroke = stroke.replace(/C\s*\((\d+,\d+)\)\s*\((\d+,\d+)\)\s*\((\d+,\d+)\)/g, (c, p1, p2, p3) => { - // return `C (${p1}) (${p2}) (${p3}) (${p3})`; - // }); - // const coordStrings = stroke.match(/(\d+,\d+)/g); - // const coords: InkData = []; - // if (coordStrings) { - // coordStrings.forEach(coord => { - // const xy = coord.split(','); - // coords.push({ X: parseInt(xy[0]), Y: parseInt(xy[1]) }); - // }); - // coords.pop(); - // parsedSegments.push(coords); - // } - // console.log('coords', coords); - // }); - // this._addToDocFunc(parsedSegments); } catch (err) { console.error('GPT call failed', err); } - - this.setIsLoading(false); - this.setUserInput(''); this.hideSmartDrawHandler(); + this._showRegenerate = true; }; - changeDrawingType = () => { - if (this._drawingTypeIcon === 'star') { - this._drawingTypeIcon = 'splotch'; - this._drawingTypeToolTip = 'Create Rounded Drawing'; - this._alpha = 0.2; - } else { - this._drawingTypeIcon = 'star'; - this._drawingTypeToolTip = 'Create Geometric Drawing'; - this._alpha = 0; - } + regenerate = (e: React.MouseEvent) => { + this.drawWithGPT(e, { X: this._lastX, Y: this._lastY }, `Regenerate the item "${this._userInput}"`, true); }; render() { @@ -144,7 +137,16 @@ export class SmartDrawHandler extends ObservableReactComponent<{}> { color: SettingsManager.userColor, }}>
- } color={MarqueeOptionsMenu.Instance.userColor} style={{ width: '19px' }} /> + { + this.hideSmartDrawHandler(); + this.hideRegenerate(); + }} + icon={} + color={SettingsManager.userColor} + style={{ width: '19px' }} + /> { }} placeholder="Enter item to draw" /> - } color={MarqueeOptionsMenu.Instance.userColor} style={{ width: '14px' }} onClick={this.changeDrawingType} /> - {/* } - color={MarqueeOptionsMenu.Instance.userColor} + } + color={SettingsManager.userColor} style={{ width: '14px' }} onClick={() => { - this._alpha = 0; + this._showOptions = !this._showOptions; }} - /> */} + />
+ {this._showOptions && ( + <> +
+
+ Auto color + (this._autoColor = !this._autoColor)} + /> +
+
+ Complexity + { + this._complexity = val as number; + }} + valueLabelDisplay="auto" + /> +
+
+ Size (in pixels) + { + this._size = val as number; + }} + valueLabelDisplay="auto" + /> +
+
+ + )} + + ); + } else if (this._showRegenerate) { + return ( +
+
+ } color={SettingsManager.userColor} style={{ width: '19px' }} /> + : } + color={SettingsManager.userColor} onClick={e => { - this.drawWithGPT({ X: e.clientX, Y: e.clientY }, this._userInput); + this.regenerate(e); }} />
-- cgit v1.2.3-70-g09d2