From fb87cdaee4c778aff9202042e4c6070fa52919e3 Mon Sep 17 00:00:00 2001 From: CrossPr0duct Date: Mon, 24 Feb 2025 17:20:22 -0500 Subject: [PATCH] Painting Works. --- frontend/realtime/src/KonvaApp/Engine.ts | 16 ++-- .../realtime/src/KonvaApp/Nodes/PaintNode.ts | 92 +++++++++++++++++++ .../RenderingPrimitives/RealTimeDrawEngine.ts | 60 +++++++----- 3 files changed, 138 insertions(+), 30 deletions(-) create mode 100644 frontend/realtime/src/KonvaApp/Nodes/PaintNode.ts diff --git a/frontend/realtime/src/KonvaApp/Engine.ts b/frontend/realtime/src/KonvaApp/Engine.ts index e3362f8b8d..d3ac1541ad 100644 --- a/frontend/realtime/src/KonvaApp/Engine.ts +++ b/frontend/realtime/src/KonvaApp/Engine.ts @@ -113,13 +113,7 @@ export class Engine { this.offScreenCanvas = new OffscreenCanvas(0, 0); - this.realTimeDrawEngine = new RealTimeDrawEngine({ - width: VideoResolutions.SQUARE_1024.width, - height: VideoResolutions.SQUARE_1024.height, - mediaLayerRef: this.mediaLayer, - bgLayerRef: this.bgLayer, - offScreenCanvas: this.offScreenCanvas, - }); + // Collection of all Nodes this.nodesManager = new NodesManager(); @@ -129,6 +123,14 @@ export class Engine { mediaLayerRef: this.mediaLayer, }); + this.realTimeDrawEngine = new RealTimeDrawEngine({ + width: VideoResolutions.SQUARE_1024.width, + height: VideoResolutions.SQUARE_1024.height, + mediaLayerRef: this.mediaLayer, + offScreenCanvas: this.offScreenCanvas, + selectionManagerRef:this.selectionManager + }); + // Selector Square to select Nodes this.selectorSquare = new SelectorSquare({ captureCanvasRef: this.realTimeDrawEngine.captureCanvas, diff --git a/frontend/realtime/src/KonvaApp/Nodes/PaintNode.ts b/frontend/realtime/src/KonvaApp/Nodes/PaintNode.ts new file mode 100644 index 0000000000..2212d05ec5 --- /dev/null +++ b/frontend/realtime/src/KonvaApp/Nodes/PaintNode.ts @@ -0,0 +1,92 @@ +import Konva from "konva"; +import { SelectionManager } from "../NodesManagers"; +import { Position, Size, NodeData, TransformationData } from "../types"; +import { NodeType } from "./constants"; +import { NodeUtilities } from "./NodeUtilities"; +import { NetworkedNode } from "./NetworkedNode"; + +interface PaintNodeConstructor { + canvasElement:HTMLCanvasElement; + lineBounds: { + width: number; + height: number; + x: number; + y: number; + }; + mediaLayerRef: Konva.Layer; + selectionManagerRef: SelectionManager; + + loaded: () => Promise; +} + +export class PaintNode extends NetworkedNode { + retry(): void { + throw new Error("Method not implemented."); + } + + constructor({ + canvasElement, + lineBounds, + mediaLayerRef, + selectionManagerRef, + loaded, + }: PaintNodeConstructor) { + + const cl = canvasElement + + // Convert circle to image + const dataURL = cl.toDataURL(); + const imageObj = new Image(); + imageObj.src = dataURL; + + const paintShape = new Konva.Image({ + x: lineBounds.x, + y: lineBounds.y, + width: lineBounds.width, + height: lineBounds.height, + image: imageObj, + listening: false, // Disable event listening to allow captureCanvas to receive events + draggable: true, + stroke: '#000000', + strokeWidth: 2, + dash: [5, 5], // Creates dotted outline + globalCompositeOperation: 'source-over', + fill: 'transparent' + }); + + imageObj.onload = () => { + this.mediaLayerRef.draw(); + loaded(); + }; + + super({ + selectionManagerRef: selectionManagerRef, + mediaLayerRef: mediaLayerRef, + kNode: paintShape, + }); + + this.mediaLayerRef.add(this.kNode); + this.listenToBaseKNode(); + this.mediaLayerRef.draw(); + } + + public getNodeData(canvasPosition: Position) { + const data: NodeData = { + type: NodeType.SHAPE, + transform: { + position: { + x: this.kNode.position().x - canvasPosition.x, + y: this.kNode.position().y - canvasPosition.y, + }, + size: this.kNode.size(), + rotation: this.kNode.rotation(), + scale: { + x: this.kNode.scaleX(), + y: this.kNode.scaleY(), + }, + zIndex: this.kNode.getZIndex(), + }, + }; + return data; + } +} diff --git a/frontend/realtime/src/KonvaApp/RenderingPrimitives/RealTimeDrawEngine.ts b/frontend/realtime/src/KonvaApp/RenderingPrimitives/RealTimeDrawEngine.ts index 0a2cbd620c..fd28e81423 100644 --- a/frontend/realtime/src/KonvaApp/RenderingPrimitives/RealTimeDrawEngine.ts +++ b/frontend/realtime/src/KonvaApp/RenderingPrimitives/RealTimeDrawEngine.ts @@ -11,12 +11,15 @@ import { MediaNode } from "../types"; import { RenderTask } from "./RenderTask"; import { OffScreenSceneCanvas } from "./OffScreenSceneCanvas"; +import { Image } from "@tauri-apps/api/image"; +import { SelectionManager } from "../NodesManagers"; +import { PaintNode } from "../Nodes/PaintNode"; // https://www.aiseesoft.com/resource/phone-aspect-ratio-screen-resolution.html#:~:text=16%3A9%20Aspect%20Ratio export class RealTimeDrawEngine { private videoNodes: VideoNode[]; - private imageNodes: (ImageNode | TextNode | ShapeNode)[]; + private imageNodes: (ImageNode | TextNode | ShapeNode | PaintNode)[]; private offScreenCanvas: OffscreenCanvas; private outputBitmap: ImageBitmap | undefined; @@ -48,7 +51,6 @@ export class RealTimeDrawEngine { public currentStrength: number; - // Paint Color // paint Brush Size // has to exit out of paint mode when shape or image are used. @@ -56,20 +58,26 @@ export class RealTimeDrawEngine { public paintBrushSize: number = 5; public isPaintMode: boolean = false; + private selectionManagerRef: SelectionManager; + constructor({ width, height, mediaLayerRef, offScreenCanvas, + selectionManagerRef, }: { width: number; height: number; mediaLayerRef: Konva.Layer; offScreenCanvas: OffscreenCanvas; + selectionManagerRef: SelectionManager; }) { this.videoLoadingCanvas = undefined; this.videoNodes = []; this.imageNodes = []; + + this.selectionManagerRef = selectionManagerRef; // TODO: Make this dynamic and update this on change of canvas. @@ -210,26 +218,11 @@ export class RealTimeDrawEngine { height: lineBounds.height, pixelRatio: 1, }); - - // Create a new Konva Image with the drawn content - const drawingImage = new Konva.Image({ - x: lineBounds.x, - y: lineBounds.y, - width: lineBounds.width, - height: lineBounds.height, - image: lineCanvas, - listening: false, // Disable event listening to allow captureCanvas to receive events - draggable: true, - stroke: '#000000', - strokeWidth: 2, - dash: [5, 5], // Creates dotted outline - globalCompositeOperation: 'source-over', - fill: 'transparent' // Add green background - }); - + + this.createPaintNode(lineCanvas,lineBounds) // Add the image to the drawingsLayer - this.drawingsLayer.add(drawingImage); + // this.drawingsLayer.add(drawingImage); // Clean up lineToConvert.destroy(); @@ -491,15 +484,36 @@ export class RealTimeDrawEngine { await this.render(); }; + public async createPaintNode(canvasElement: HTMLCanvasElement, lineBounds:{ + width: number; + height: number; + x: number; + y: number;}){ + // Create an ImageNode from the Konva.Image + var node = new PaintNode({ + canvasElement:canvasElement, + lineBounds:lineBounds, + canvasPosition: this.captureCanvas.position(), + canvasSize: this.captureCanvas.size(), + mediaLayerRef: this.mediaLayerRef, + selectionManagerRef: this.selectionManagerRef, + loaded: async () => { + await this.render(); + }}) + await this.addNodes(node); + } + public async addNodes(node: MediaNode) { - - if (node instanceof ImageNode || node instanceof TextNode || node instanceof ShapeNode) { + if (node instanceof ImageNode || + node instanceof TextNode || + node instanceof ShapeNode || + node instanceof PaintNode) + { console.debug("Adding node:", node); this.imageNodes.push(node); console.log(this.imageNodes) node.kNode.on("dragend", this.handleNodeDragEnd); } - } public removeNodes(node: MediaNode) {