From 57fd540054ac7c102fec4b0da73854f018f8bbd3 Mon Sep 17 00:00:00 2001 From: Zachary Zhang Date: Wed, 10 Jul 2024 23:15:09 -0400 Subject: working version of gpt handwriting recognition --- src/.DS_Store | Bin 10244 -> 14340 bytes 1 file changed, 0 insertions(+), 0 deletions(-) (limited to 'src/.DS_Store') diff --git a/src/.DS_Store b/src/.DS_Store index 75cff7b55..532210ecf 100644 Binary files a/src/.DS_Store and b/src/.DS_Store differ -- cgit v1.2.3-70-g09d2 From a1960941edc43d71508ec5cb244f453eb06cf2e1 Mon Sep 17 00:00:00 2001 From: Zachary Zhang Date: Tue, 23 Jul 2024 12:04:09 -0400 Subject: adsf --- src/.DS_Store | Bin 14340 -> 14340 bytes 1 file changed, 0 insertions(+), 0 deletions(-) (limited to 'src/.DS_Store') diff --git a/src/.DS_Store b/src/.DS_Store index 532210ecf..19eea5fce 100644 Binary files a/src/.DS_Store and b/src/.DS_Store differ -- cgit v1.2.3-70-g09d2 From 8ca26551622d36b7856f5c1865498fa9e5d888b5 Mon Sep 17 00:00:00 2001 From: eleanor-park Date: Thu, 25 Jul 2024 23:36:52 -0400 Subject: cleaned up smart draw & palette --- src/.DS_Store | Bin 14340 -> 14340 bytes src/client/documents/Documents.ts | 4 ++ src/client/util/CurrentUserUtils.ts | 3 +- src/client/util/bezierFit.ts | 2 +- src/client/views/DocumentButtonBar.tsx | 13 ++-- src/client/views/ViewBoxInterface.ts | 2 +- .../collectionFreeForm/CollectionFreeFormView.tsx | 8 +-- src/client/views/smartdraw/AnnotationPalette.tsx | 80 +++++++++++---------- src/client/views/smartdraw/SmartDrawHandler.tsx | 47 ++++++------ 9 files changed, 83 insertions(+), 76 deletions(-) (limited to 'src/.DS_Store') diff --git a/src/.DS_Store b/src/.DS_Store index 19eea5fce..9130ba97c 100644 Binary files a/src/.DS_Store and b/src/.DS_Store differ diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index ff95e38bd..d311ad971 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -1009,6 +1009,10 @@ export namespace Docs { return InstanceFromProto(Prototypes.get(DocumentType.DATAVIZ), new CsvField(url), { title: 'Data Viz', type: 'dataviz', ...options }, undefined, undefined, undefined, overwriteDoc); } + export function AnnoPaletteDocument(options?: DocumentOptions) { + return InstanceFromProto(Prototypes.get(DocumentType.ANNOPALETTE), new List([Doc.MyAnnos]), { ...(options || {}) }); + } + export function DockDocument(documents: Array, config: string, options: DocumentOptions, id?: string) { const ret = InstanceFromProto(Prototypes.get(DocumentType.COL), new List(documents), { treeView_FreezeChildren: 'remove|add', ...options, type_collection: CollectionViewType.Docking, dockingConfig: config }, id); documents.map(c => Doc.SetContainer(c, ret)); diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 6b59828eb..8c7d40158 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -176,7 +176,7 @@ export class CurrentUserUtils { static setupLightboxDrawingPreviews(doc: Doc, field="myLightboxDrawings") { const reqdOpts:DocumentOptions = { - title: "Preview", _layout_fitWidth: true, childLayoutFitWidth: true, + title: "Preview", _header_height: 0, _layout_fitWidth: true, childLayoutFitWidth: true, }; const reqdScripts = {}; const drawings = DocCast(doc[field]); @@ -392,6 +392,7 @@ pie title Minerals in my tap water {key: "Script", creator: opts => Docs.Create.ScriptingDocument(null, opts), opts: { _width: 200, _height: 250, }}, {key: "DataViz", creator: opts => Docs.Create.DataVizDocument("/users/rz/Downloads/addresses.csv", opts), opts: { _width: 300, _height: 300 }}, {key: "Chat", creator: Docs.Create.ChatDocument, opts: { _width: 300, _height: 300, }}, + {key: "AnnoPalette",creator: Docs.Create.AnnoPaletteDocument, opts: {_width: 300, _height: 300, _dropAction: dropActionType.move }}, {key: "Header", creator: headerTemplate, opts: { _width: 300, _height: 120, _header_pointerEvents: "all", _header_height: 50, _header_fontSize: 9,_layout_autoHeightMargins: 50, _layout_autoHeight: true, treeView_HideUnrendered: true}}, {key: "ViewSlide", creator: slideView, opts: { _width: 400, _height: 300, _xMargin: 3, _yMargin: 3,}}, {key: "Trail", creator: Docs.Create.PresDocument, opts: { _width: 400, _height: 30, _type_collection: CollectionViewType.Stacking, dropAction: dropActionType.embed, treeView_HideTitle: true, _layout_fitWidth:true, layout_boxShadow: "0 0" }}, diff --git a/src/client/util/bezierFit.ts b/src/client/util/bezierFit.ts index 6a4577506..693676bc3 100644 --- a/src/client/util/bezierFit.ts +++ b/src/client/util/bezierFit.ts @@ -694,7 +694,7 @@ export function SVGToBezier(name: SVGType, attributes: any): Point[] { const matches: RegExpMatchArray[] = Array.from( attributes.d.matchAll(/Q(-?\d+\.?\d*),(-?\d+\.?\d*) (-?\d+\.?\d*),(-?\d+\.?\d*)|C(-?\d+\.?\d*),(-?\d+\.?\d*) (-?\d+\.?\d*),(-?\d+\.?\d*) (-?\d+\.?\d*),(-?\d+\.?\d*)|L(-?\d+\.?\d*),(-?\d+\.?\d*)/g) ); - let lastPt: Point = { X: 0, Y: 0 }; + let lastPt: Point; matches.forEach(match => { if (match[0].startsWith('Q')) { coordList.push({ X: parseInt(match[1]), Y: parseInt(match[2]) }); diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx index 6950bec14..15e90ac2a 100644 --- a/src/client/views/DocumentButtonBar.tsx +++ b/src/client/views/DocumentButtonBar.tsx @@ -243,23 +243,18 @@ export class DocumentButtonBar extends ObservableReactComponent<{ views: () => ( ); } - @observable _annoSaved: boolean = false; - @undoBatch - saveAnno = action((targetDoc: Doc) => { - // targetDoc.savedAsAnno = true; - this._annoSaved = true; - // AnnotationPalette.addToPalette(targetDoc); + saveAnno = action(async (targetDoc: Doc) => { + await AnnotationPalette.addToPalette(targetDoc); }); @computed get saveAnnoButton() { const targetDoc = this.view0?.Document; - if (targetDoc && targetDoc.savedAsAnno) this._annoSaved = true; return !targetDoc ? null : ( - {this._annoSaved ? 'Saved as Annotation!' : 'Save to Annotation Palette'}}> + {targetDoc.savedAsAnno ? 'Saved as Annotation!' : 'Save to Annotation Palette'}}>
this.saveAnno(targetDoc)}> - +
); diff --git a/src/client/views/ViewBoxInterface.ts b/src/client/views/ViewBoxInterface.ts index c633f34fb..ea5f3dd27 100644 --- a/src/client/views/ViewBoxInterface.ts +++ b/src/client/views/ViewBoxInterface.ts @@ -19,7 +19,7 @@ export abstract class ViewBoxInterface

extends ObservableReactComponent void; // moves contents of collection to parent - updateIcon?: () => void; // updates the icon representation of the document + updateIcon?: (usePanelDimensions?: boolean) => void; // updates the icon representation of the document getAnchor?: (addAsAnnotation: boolean, pinData?: PinProps) => Doc; // returns an Anchor Doc that represents the current state of the doc's componentview (e.g., the current playhead location of a an audio/video box) restoreView?: (viewSpec: Doc) => boolean; scrollPreview?: (docView: DocumentView, doc: Doc, focusSpeed: number, options: FocusViewOptions) => Opt; // returns the duration of the focus diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index f2b94fabc..a298e6ac4 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -1301,7 +1301,7 @@ export class CollectionFreeFormView extends CollectionSubView disposer?.()); } - updateIcon = () => + updateIcon = (usePanelDimensions?: boolean) => UpdateIcon( this.layoutDoc[Id] + '-icon' + new Date().getTime(), this.DocumentView?.().ContentDiv!, - NumCast(this.layoutDoc._width), - NumCast(this.layoutDoc._height), + usePanelDimensions ? this._props.PanelWidth() : NumCast(this.layoutDoc._width), + usePanelDimensions ? this._props.PanelHeight() : NumCast(this.layoutDoc._height), this._props.PanelWidth(), this._props.PanelHeight(), 0, diff --git a/src/client/views/smartdraw/AnnotationPalette.tsx b/src/client/views/smartdraw/AnnotationPalette.tsx index 83f173fcb..ec4279e3e 100644 --- a/src/client/views/smartdraw/AnnotationPalette.tsx +++ b/src/client/views/smartdraw/AnnotationPalette.tsx @@ -11,7 +11,7 @@ import { returnAll, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnOn import { ActiveInkWidth, Doc, DocListCast, Opt, StrListCast } from '../../../fields/Doc'; import { DocData } from '../../../fields/DocSymbols'; import { InkData, InkField } from '../../../fields/InkField'; -import { BoolCast, DocCast, ImageCast, NumCast } from '../../../fields/Types'; +import { BoolCast, DocCast, ImageCast } from '../../../fields/Types'; import { emptyFunction, unimplementedFunction } from '../../../Utils'; import { Docs } from '../../documents/Documents'; import { makeUserTemplateButton, makeUserTemplateImage } from '../../util/DropConverter'; @@ -19,7 +19,7 @@ import { SettingsManager } from '../../util/SettingsManager'; import { Transform } from '../../util/Transform'; import { undoable, undoBatch } from '../../util/UndoManager'; import { CollectionFreeFormView, MarqueeOptionsMenu, MarqueeView } from '../collections/collectionFreeForm'; -import { ActiveArrowEnd, ActiveArrowStart, ActiveDash, ActiveFillColor, ActiveInkBezierApprox, ActiveIsInkMask, DocumentView, DocumentViewInternal } from '../nodes/DocumentView'; +import { ActiveArrowEnd, ActiveArrowStart, ActiveDash, ActiveFillColor, ActiveInkBezierApprox, ActiveInkColor, ActiveIsInkMask, DocumentView, DocumentViewInternal } from '../nodes/DocumentView'; import { FieldView } from '../nodes/FieldView'; import { ObservableReactComponent } from '../ObservableReactComponent'; import { DefaultStyleProvider } from '../StyleProvider'; @@ -30,9 +30,12 @@ import { ImageField } from '../../../fields/URLField'; import { CollectionCarousel3DView } from '../collections/CollectionCarousel3DView'; import { Copy } from '../../../fields/FieldSymbols'; +interface AnnotationPaletteProps { + Document: Doc; +} + @observer -export class AnnotationPalette extends ObservableReactComponent<{ Document: Doc }> { - static Instance: AnnotationPalette; +export class AnnotationPalette extends ObservableReactComponent { @observable private _paletteMode: 'create' | 'view' = 'view'; @observable private _userInput: string = ''; @observable private _isLoading: boolean = false; @@ -46,7 +49,6 @@ export class AnnotationPalette extends ObservableReactComponent<{ Document: Doc constructor(props: any) { super(props); makeObservable(this); - AnnotationPalette.Instance = this; } public static LayoutString(fieldKey: string) { @@ -102,30 +104,35 @@ export class AnnotationPalette extends ObservableReactComponent<{ Document: Doc this._canInteract = true; this._opts = { text: '', complexity: 5, size: 200, autoColor: true, x: 0, y: 0 }; this._gptRes = []; + this._props.Document[DocData].data = undefined; }; public static addToPalette = async (doc: Doc) => { - // if (!doc.savedAsAnno) { - // const clone = await Doc.MakeClone(doc); - // clone.clone.title = doc.title; - // const image = (await this.getIcon(doc))?.[Copy](); - // if (image) { - // const imageTemplate = makeUserTemplateImage(clone.clone, image); - // Doc.AddDocToList(Doc.MyAnnos, 'data', imageTemplate); - // doc.savedAsAnno = true; - // } - // this.resetPalette(true); - // } + if (!doc.savedAsAnno) { + const clone = await Doc.MakeClone(doc); + clone.clone.title = doc.title; + const image = (await AnnotationPalette.getIcon(doc))?.[Copy](); + if (image) { + const imageTemplate = makeUserTemplateImage(clone.clone, image); + Doc.AddDocToList(Doc.MyAnnos, 'data', imageTemplate); + doc.savedAsAnno = true; + } + } }; - // @action - // displayPalette = (display: boolean) => { - // this._display = display; - // }; + public static getIcon(group: Doc) { + const docView = DocumentView.getDocumentView(group); + if (docView) { + docView.ComponentView?.updateIcon?.(true); + return new Promise(res => setTimeout(() => res(ImageCast(docView.Document.icon)), 1000)); + } + return undefined; + } @undoBatch generateDrawing = action(async () => { this._isLoading = true; + this._props.Document[DocData].data = undefined; for (var i = 0; i < 3; i++) { try { SmartDrawHandler.Instance._addFunc = this.createDrawing; @@ -154,7 +161,7 @@ export class AnnotationPalette extends ObservableReactComponent<{ Document: Doc strokeList.forEach((stroke: [InkData, string, string]) => { const bounds = InkField.getBounds(stroke[0]); - const inkWidth = ActiveInkWidth(); + const inkWidth = Math.min(5, ActiveInkWidth()); const inkDoc = Docs.Create.InkDocument( stroke[0], { title: 'stroke', @@ -164,7 +171,7 @@ export class AnnotationPalette extends ObservableReactComponent<{ Document: Doc _height: bounds.height + inkWidth, stroke_showLabel: BoolCast(Doc.UserDoc().activeInkHideTextLabels)}, // prettier-ignore inkWidth, - stroke[1], + opts.autoColor ? stroke[1] : ActiveInkColor(), ActiveInkBezierApprox(), stroke[2] === 'none' ? ActiveFillColor() : stroke[2], ActiveArrowStart(), @@ -177,28 +184,30 @@ export class AnnotationPalette extends ObservableReactComponent<{ Document: Doc const collection = MarqueeView.getCollection(drawing, undefined, true, { left: 1, top: 1, width: 1, height: 1 }); if (collection) { + collection[DocData].freeform_fitContentsToBox = true; Doc.AddDocToList(this._props.Document, 'data', collection); } }; saveDrawing = async () => { - // const cIndex: number = this._drawingCarousel.carousel_index as number; - // const focusedDrawing = this._drawings[cIndex]; - // const docData = focusedDrawing[DocData]; - // docData.title = this._opts.text.match(/^(.*?)~~~.*$/)?.[1] || this._opts.text; - // docData.drawingInput = this._opts.text; - // docData.drawingComplexity = this._opts.complexity; - // docData.drawingColored = this._opts.autoColor; - // docData.drawingSize = this._opts.size; - // docData.drawingData = this._gptRes[cIndex]; - // docData.width = this._opts.size; - // await AnnotationPalette.addToPalette(focusedDrawing); + const cIndex: number = this._props.Document.carousel_index as number; + const focusedDrawing = DocListCast(this._props.Document.data)[cIndex]; + const docData = focusedDrawing[DocData]; + docData.title = this._opts.text.match(/^(.*?)~~~.*$/)?.[1] || this._opts.text; + docData.drawingInput = this._opts.text; + docData.drawingComplexity = this._opts.complexity; + docData.drawingColored = this._opts.autoColor; + docData.drawingSize = this._opts.size; + docData.drawingData = this._gptRes[cIndex]; + docData.width = this._opts.size; + await AnnotationPalette.addToPalette(focusedDrawing); + this.resetPalette(true); }; async getIcon(group: Doc) { const docView = DocumentView.getDocumentView(group); if (docView) { - docView.ComponentView?.updateIcon?.(); + docView.ComponentView?.updateIcon?.(true); return new Promise(res => setTimeout(() => res(ImageCast(docView.Document.icon)), 1000)); } return undefined; @@ -206,7 +215,7 @@ export class AnnotationPalette extends ObservableReactComponent<{ Document: Doc render() { return ( -

+
e.stopPropagation()}> {this._paletteMode === 'view' && ( <> e.stopPropagation()} onChange={e => { this.setUserInput(e.target.value); }} diff --git a/src/client/views/smartdraw/SmartDrawHandler.tsx b/src/client/views/smartdraw/SmartDrawHandler.tsx index ca65ea388..c842551c3 100644 --- a/src/client/views/smartdraw/SmartDrawHandler.tsx +++ b/src/client/views/smartdraw/SmartDrawHandler.tsx @@ -51,6 +51,7 @@ export class SmartDrawHandler extends ObservableReactComponent<{}> { 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; constructor(props: any) { super(props); @@ -152,39 +153,37 @@ export class SmartDrawHandler extends ObservableReactComponent<{}> { this._showEditBox = false; } else { this._showOptions = false; - await this.drawWithGPT({ X: this._pageX, Y: this._pageY }, this._userInput, this._complexity, this._size, this._autoColor); + try { + await this.drawWithGPT({ X: this._pageX, Y: this._pageY }, this._userInput, this._complexity, this._size, this._autoColor); + this._showRegenerate = true; + this.hideSmartDrawHandler(); + } catch (err) { + if (this._errorOccurredOnce) { + console.error('GPT call failed', err); + this._errorOccurredOnce = false; + } else { + this._errorOccurredOnce = true; + await this.drawWithGPT({ X: this._pageX, Y: this._pageY }, this._userInput, this._complexity, this._size, this._autoColor); + } + } } this._isLoading = false; this._canInteract = true; }; - _errorOccurredOnce = false; @action drawWithGPT = async (startPt: { X: number; Y: number }, input: string, complexity: number, size: number, autoColor: boolean) => { if (input === '') return; this._lastInput = { text: input, complexity: complexity, size: size, autoColor: autoColor, x: startPt.X, y: startPt.Y }; - - try { - const res = await gptAPICall(`"${input}", "${complexity}", "${size}"`, GPTCallType.DRAW, undefined, true); - if (!res) { - console.error('GPT call failed'); - return; - } - console.log(res); - const strokeData = await this.parseResponse(res, startPt, false, autoColor); - this._errorOccurredOnce = false; - this.hideSmartDrawHandler(); - this._showRegenerate = true; - return strokeData; - } catch (err) { - if (this._errorOccurredOnce) { - console.error('GPT call failed', err); - this._errorOccurredOnce = false; - } else { - this._errorOccurredOnce = true; - this.drawWithGPT(startPt, input, complexity, size, autoColor); - } + const res = await gptAPICall(`"${input}", "${complexity}", "${size}"`, GPTCallType.DRAW, undefined, true); + if (!res) { + console.error('GPT call failed'); + return; } + console.log(res); + const strokeData = await this.parseResponse(res, startPt, false, autoColor); + this._errorOccurredOnce = false; + return strokeData; }; @action @@ -212,7 +211,7 @@ export class SmartDrawHandler extends ObservableReactComponent<{}> { return; } console.log(res); - this.parseResponse(res, { X: this._lastInput.x, Y: this._lastInput.y }, true, lastInput?.autoColor || this._autoColor); + await this.parseResponse(res, { X: this._lastInput.x, Y: this._lastInput.y }, true, lastInput?.autoColor || this._autoColor); } catch (err) { console.error('GPT call failed', err); } -- cgit v1.2.3-70-g09d2 From 06ab521c759e44a26be58fdf7ffc8d790e551236 Mon Sep 17 00:00:00 2001 From: eleanor-park Date: Tue, 1 Oct 2024 18:45:01 -0400 Subject: working on image bug for a while --- src/.DS_Store | Bin 10244 -> 10244 bytes src/client/views/MainView.tsx | 1 + .../views/nodes/generativeFill/GenerativeFill.tsx | 104 ++++++++++++++++++--- .../nodes/generativeFill/GenerativeFillButtons.tsx | 34 ++++++- .../generativeFillUtils/BrushHandler.ts | 16 +++- 5 files changed, 138 insertions(+), 17 deletions(-) (limited to 'src/.DS_Store') diff --git a/src/.DS_Store b/src/.DS_Store index 426a2ee90..9b66f8d8e 100644 Binary files a/src/.DS_Store and b/src/.DS_Store differ diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 41a933431..54e049c04 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -408,6 +408,7 @@ export class MainView extends ObservableReactComponent { fa.faArrowsAltV, fa.faTimesCircle, fa.faThumbtack, + fa.faScissors, fa.faTree, fa.faTv, fa.faUndoAlt, diff --git a/src/client/views/nodes/generativeFill/GenerativeFill.tsx b/src/client/views/nodes/generativeFill/GenerativeFill.tsx index 6d8ba9222..261eb4bb4 100644 --- a/src/client/views/nodes/generativeFill/GenerativeFill.tsx +++ b/src/client/views/nodes/generativeFill/GenerativeFill.tsx @@ -21,19 +21,16 @@ import { CollectionFreeFormView } from '../../collections/collectionFreeForm'; import { ImageEditorData } from '../ImageBox'; import { OpenWhereMod } from '../OpenWhere'; import './GenerativeFill.scss'; -import Buttons from './GenerativeFillButtons'; -import { BrushHandler } from './generativeFillUtils/BrushHandler'; +import { EditButtons, CutButtons } from './GenerativeFillButtons'; +import { BrushHandler, BrushType } from './generativeFillUtils/BrushHandler'; import { APISuccess, ImageUtility } from './generativeFillUtils/ImageHandler'; import { PointerHandler } from './generativeFillUtils/PointerHandler'; import { activeColor, canvasSize, eraserColor, freeformRenderSize, newCollectionSize, offsetDistanceY, offsetX } from './generativeFillUtils/generativeFillConstants'; import { CursorData, ImageDimensions, Point } from './generativeFillUtils/generativeFillInterfaces'; import { DocumentView } from '../DocumentView'; - -// enum BrushStyle { -// ADD, -// SUBTRACT, -// MARQUEE, -// } +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { ImageField } from '../../../../fields/URLField'; +import { resolve } from 'url'; interface GenerativeFillProps { imageEditorOpen: boolean; @@ -82,6 +79,9 @@ const GenerativeFill = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addD const parentDoc = useRef(null); const childrenDocs = useRef([]); + // constants for image cutting + const cutPts = useRef([]); + // Undo and Redo const handleUndo = () => { const ctx = ImageUtility.getCanvasContext(canvasRef); @@ -161,7 +161,8 @@ const GenerativeFill = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addD x: currPoint.x - e.movementX / canvasScale, y: currPoint.y - e.movementY / canvasScale, }; - BrushHandler.createBrushPathOverlay(lastPoint, currPoint, cursorData.width / 2 / canvasScale, ctx, eraserColor /* , brushStyle === BrushStyle.SUBTRACT */); + const pts = BrushHandler.createBrushPathOverlay(lastPoint, currPoint, cursorData.width / 2 / canvasScale, ctx, eraserColor, BrushType.CUT); + cutPts.current.push(...pts); }; drawingAreaRef.current?.addEventListener('pointermove', handlePointerMove); @@ -278,7 +279,6 @@ const GenerativeFill = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addD const maskBlob = await ImageUtility.canvasToBlob(canvasMask); const imgBlob = await ImageUtility.canvasToBlob(canvasOriginalImg); const res = await ImageUtility.getEdit(imgBlob, maskBlob, input !== '' ? input + ' in the same style' : 'Fill in the image in the same style', 2); - // const res = await ImageUtility.mockGetEdit(img.src); // create first image if (!newCollectionRef.current) { @@ -334,6 +334,68 @@ const GenerativeFill = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addD setLoading(false); }; + const cutImage = async () => { + const img = currImg.current; + const canvas = canvasRef.current; + if (!canvas || !img) return; + canvas.width = img.naturalWidth; + canvas.height = img.naturalHeight; + const ctx = ImageUtility.getCanvasContext(canvasRef); + if (!ctx) return; + ctx.globalCompositeOperation = 'source-over'; + setLoading(true); + setEdited(true); + // get the original image + const canvasOriginalImg = ImageUtility.getCanvasImg(img); + if (!canvasOriginalImg) return; + // draw the image onto the canvas + ctx.drawImage(img, 0, 0); + // get the mask which i assume is the thing the user draws on + // const canvasMask = ImageUtility.getCanvasMask(canvas, canvasOriginalImg); + // if (!canvasMask) return; + // canvasMask.width = canvas.width; + // canvasMask.height = canvas.height; + // now put the user's path around the mask + if (cutPts.current.length) { + ctx.beginPath(); + ctx.moveTo(cutPts.current[0].x, cutPts.current[0].y); // later check edge case where cutPts is empty + for (let i = 0; i < cutPts.current.length; i++) { + ctx.lineTo(cutPts.current[i].x, cutPts.current[i].y); + } + ctx.closePath(); + ctx.stroke(); + ctx.fill(); + // ctx.clip(); + } + const url = canvas.toDataURL(); // this does the same thing as convert img to canvasurl + if (!newCollectionRef.current) { + if (!isNewCollection && imageRootDoc) { + // if the parent hasn't been set yet + if (!parentDoc.current) parentDoc.current = imageRootDoc; + } else { + if (!(originalImg.current && imageRootDoc)) return; + // create new collection and add it to the view + newCollectionRef.current = Docs.Create.FreeformDocument([], { + x: NumCast(imageRootDoc.x) + NumCast(imageRootDoc._width) + offsetX, + y: NumCast(imageRootDoc.y), + _width: newCollectionSize, + _height: newCollectionSize, + title: 'Image edit collection', + }); + DocUtils.MakeLink(imageRootDoc, newCollectionRef.current, { link_relationship: 'Image Edit Version History' }); + // opening new tab + CollectionDockingView.AddSplit(newCollectionRef.current, OpenWhereMod.right); + } + } + const image = new Image(); + image.src = url; + await createNewImgDoc(image, true); + // add the doc to the main freeform + // eslint-disable-next-line no-use-before-define + setLoading(false); + cutPts.current.length = 0; + }; + // adjusts all the img positions to be aligned const adjustImgPositions = () => { if (!parentDoc.current) return; @@ -439,6 +501,7 @@ const GenerativeFill = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addD

Image Editor

+ {/* } /> */}
- + + } onClick={handleViewClose} />
@@ -526,6 +590,24 @@ const GenerativeFill = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addD }} />
+
e.stopPropagation()} style={{ height: 225, width: '100%', display: 'flex', justifyContent: 'center', cursor: 'pointer' }}> + { + setCursorData(prev => ({ ...prev, width: val as number })); + }} + /> +
{/* Edits thumbnails */}
diff --git a/src/client/views/nodes/generativeFill/GenerativeFillButtons.tsx b/src/client/views/nodes/generativeFill/GenerativeFillButtons.tsx index d1f68ee0e..fe22b273d 100644 --- a/src/client/views/nodes/generativeFill/GenerativeFillButtons.tsx +++ b/src/client/views/nodes/generativeFill/GenerativeFillButtons.tsx @@ -6,12 +6,12 @@ import { AiOutlineInfo } from 'react-icons/ai'; import { activeColor } from './generativeFillUtils/generativeFillConstants'; interface ButtonContainerProps { - getEdit: () => Promise; + onClick: () => Promise; loading: boolean; onReset: () => void; } -function Buttons({ loading, getEdit, onReset }: ButtonContainerProps) { +export function EditButtons({ loading, onClick: getEdit, onReset }: ButtonContainerProps) { return (
+ ); +} diff --git a/src/client/views/nodes/generativeFill/generativeFillUtils/BrushHandler.ts b/src/client/views/nodes/generativeFill/generativeFillUtils/BrushHandler.ts index 16d529d93..8a66d7347 100644 --- a/src/client/views/nodes/generativeFill/generativeFillUtils/BrushHandler.ts +++ b/src/client/views/nodes/generativeFill/generativeFillUtils/BrushHandler.ts @@ -1,6 +1,12 @@ import { GenerativeFillMathHelpers } from './GenerativeFillMathHelpers'; import { eraserColor } from './generativeFillConstants'; import { Point } from './generativeFillInterfaces'; +import { points } from '@turf/turf'; + +export enum BrushType { + GEN_FILL, + CUT, +} export class BrushHandler { static brushCircleOverlay = (x: number, y: number, brushRadius: number, ctx: CanvasRenderingContext2D, fillColor: string /* , erase: boolean */) => { @@ -14,12 +20,16 @@ export class BrushHandler { ctx.closePath(); }; - static createBrushPathOverlay = (startPoint: Point, endPoint: Point, brushRadius: number, ctx: CanvasRenderingContext2D, fillColor: string /* , erase: boolean */) => { + static createBrushPathOverlay = (startPoint: Point, endPoint: Point, brushRadius: number, ctx: CanvasRenderingContext2D, fillColor: string, brushType: BrushType) => { const dist = GenerativeFillMathHelpers.distanceBetween(startPoint, endPoint); - + const pts: Point[] = []; for (let i = 0; i < dist; i += 5) { const s = i / dist; - BrushHandler.brushCircleOverlay(startPoint.x * (1 - s) + endPoint.x * s, startPoint.y * (1 - s) + endPoint.y * s, brushRadius, ctx, fillColor /* , erase */); + const x = startPoint.x * (1 - s) + endPoint.x * s; + const y = startPoint.y * (1 - s) + endPoint.y * s; + pts.push({ x: startPoint.x, y: startPoint.y }); + BrushHandler.brushCircleOverlay(x, y, brushRadius, ctx, fillColor); } + return pts; }; } -- cgit v1.2.3-70-g09d2