aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/.DS_Storebin10244 -> 10244 bytes
-rw-r--r--src/client/views/MainView.tsx1
-rw-r--r--src/client/views/nodes/generativeFill/GenerativeFill.tsx104
-rw-r--r--src/client/views/nodes/generativeFill/GenerativeFillButtons.tsx34
-rw-r--r--src/client/views/nodes/generativeFill/generativeFillUtils/BrushHandler.ts16
5 files changed, 138 insertions, 17 deletions
diff --git a/src/.DS_Store b/src/.DS_Store
index 426a2ee90..9b66f8d8e 100644
--- a/src/.DS_Store
+++ b/src/.DS_Store
Binary files differ
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index fa2e94d12..31d7e82a6 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -408,6 +408,7 @@ export class MainView extends ObservableReactComponent<object> {
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<Doc | null>(null);
const childrenDocs = useRef<Doc[]>([]);
+ // constants for image cutting
+ const cutPts = useRef<Point[]>([]);
+
// 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
<div className="generativeFillContainer" style={{ display: imageEditorOpen ? 'flex' : 'none' }}>
<div className="generativeFillControls">
<h1>Image Editor</h1>
+ {/* <IconButton text="Cut out" icon={<FontAwesomeIcon icon="scissors" />} /> */}
<div style={{ display: 'flex', alignItems: 'center', gap: '1.5rem' }}>
<FormControlLabel
control={
@@ -455,7 +518,8 @@ const GenerativeFill = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addD
labelPlacement="end"
sx={{ whiteSpace: 'nowrap' }}
/>
- <Buttons getEdit={getEdit} loading={loading} onReset={handleReset} />
+ <EditButtons onClick={getEdit} loading={loading} onReset={handleReset} />
+ <CutButtons onClick={cutImage} loading={loading} onReset={handleReset} />
<IconButton color={activeColor} tooltip="close" icon={<CgClose size="16px" />} onClick={handleViewClose} />
</div>
</div>
@@ -526,6 +590,24 @@ const GenerativeFill = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addD
}}
/>
</div>
+ <div onPointerDown={e => e.stopPropagation()} style={{ height: 225, width: '100%', display: 'flex', justifyContent: 'center', cursor: 'pointer' }}>
+ <Slider
+ sx={{
+ '& input[type="range"]': {
+ WebkitAppearance: 'slider-vertical',
+ },
+ }}
+ orientation="vertical"
+ min={1}
+ max={500}
+ defaultValue={150}
+ size="small"
+ valueLabelDisplay="auto"
+ onChange={(e: any, val: any) => {
+ setCursorData(prev => ({ ...prev, width: val as number }));
+ }}
+ />
+ </div>
</div>
{/* Edits thumbnails */}
<div className="editsBox">
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<void>;
+ onClick: () => Promise<void>;
loading: boolean;
onReset: () => void;
}
-function Buttons({ loading, getEdit, onReset }: ButtonContainerProps) {
+export function EditButtons({ loading, onClick: getEdit, onReset }: ButtonContainerProps) {
return (
<div className="generativeFillBtnContainer">
<Button text="RESET" type={Type.PRIM} color={activeColor} onClick={onReset} />
@@ -41,4 +41,32 @@ function Buttons({ loading, getEdit, onReset }: ButtonContainerProps) {
);
}
-export default Buttons;
+export function CutButtons({ loading, onClick: cutImage, onReset }: ButtonContainerProps) {
+ return (
+ <div className="generativeFillBtnContainer">
+ <Button text="RESET" type={Type.PRIM} color={activeColor} onClick={onReset} />
+ {loading ? (
+ <Button
+ text="CUT IMAGE"
+ type={Type.TERT}
+ color={activeColor}
+ icon={<ReactLoading type="spin" color="#ffffff" width={20} height={20} />}
+ iconPlacement="right"
+ onClick={() => {
+ if (!loading) cutImage();
+ }}
+ />
+ ) : (
+ <Button
+ text="CUT IMAGE"
+ type={Type.TERT}
+ color={activeColor}
+ onClick={() => {
+ if (!loading) cutImage();
+ }}
+ />
+ )}
+ <IconButton type={Type.SEC} color={activeColor} tooltip="Open Documentation" icon={<AiOutlineInfo size="16px" />} onClick={() => window.open('https://brown-dash.github.io/Dash-Documentation/features/generativeai/#editing', '_blank')} />
+ </div>
+ );
+}
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;
};
}