Feature/node refactor (#26)

* Fixed highlight bug.
Moving ImageNode to
NetworkedNodeContext base class
to make the code easier to manage with an undo stack.
This may have broken video nodes which will be fixed now.

* Selection Manager corrected to handle the states of selection correctly.
Video Node is broken needs fixes.

* Fixed the selection states.
Selects while dragging and using shift correctly.

* Ok one more bug discovered it is now fixed.

* Fixed multiselect again.

* Fixed issues with instancing

* Fixed the video playing and pausing

* Fix context menu rendering on window changing

* Send Zip Blob Back.

* Testing rendering problem by extracting the zip.

* Test bench for figuring out why this is broken is setup.

* A bit of a warzone need to go back.

* OffScreenSceneCanvas
OffScreenSceneContext
Works just fine need to merge in main

* Meme Engine ready for testing.
This commit is contained in:
ArEnSc authored and GitHub committed 2024-09-21 22:40:24 -04:00
1 parent 85c2bb822b
commit b2dd62ff23
15 files changed
+740 -577

No files matched your search

BIN
View File
Binary file not shown.
+68 -108
View File
@@ -13,7 +13,6 @@ import { ImageNode } from "./Nodes/ImageNode";
import { LoadingBarStatus } from "~/components/ui";
import { ResponseType } from "./WorkerPrimitives/SharedWorkerBase";
import { UndoStackManager } from "./UndoRedo/UndoRedoManager";
import * as ort from "onnxruntime-web";
import { CreateCommand } from "./UndoRedo/CreateCommand";
import { DeleteCommand } from "./UndoRedo/DeleteCommand";
@@ -21,6 +20,8 @@ import { RotateCommand } from "./UndoRedo/RotateCommand";
import { ScaleCommand } from "./UndoRedo/ScaleCommand";
import { TranslateCommand } from "./UndoRedo/TranslateCommand";
import { FileUtilities } from "./FileUtilities/FileUtilities";
export interface RenderingOptions {
artstyle: string;
positivePrompt: string;
@@ -117,17 +118,13 @@ export class Engine {
const media_url = `${media_api_base_url}vocodes-public${response.data.videoUrl}`;
const videoNode = new VideoNode(
uuidv4(),
this.videoLayer,
this.renderEngine.captureCanvas.position().x,
this.renderEngine.captureCanvas.position().y,
media_url,
this.selectionManager,
undefined,
undefined,
);
this.renderEngine.addNodes(videoNode);
// hide the loader
//this.renderEngine.videoLoadingCanvas.kNode.hide();
uiAccess.toolbarMain.loadingBar.hide();
@@ -138,19 +135,17 @@ export class Engine {
uiAccess.toolbarMain.loadingBar.updateProgress(
response.data.progress * 100,
);
console.log(response);
// if (response.data.zipBlob) {
// FileUtilities.downloadBlobZip(response.data.zipBlob);
// }
} else {
// throw error to retry
uiAccess.dialogueError.show({
title: "Generation Error",
message: response.data,
});
// if (!this.renderEngine.videoLoadingCanvas) {
// console.log("Did not setup video loading canvas.");
// return;
// }
//this.renderEngine.videoLoadingCanvas.kNode.hide();
uiAccess.toolbarMain.loadingBar.hide();
}
}
@@ -241,64 +236,9 @@ export class Engine {
this.stage.width(this.canvasReference.offsetWidth);
this.stage.height(this.canvasReference.offsetHeight);
this.stage.draw(); // Redraw the canvas
}
public async onnx() {
try {
//DO NOT REMOVE, NECESSARY TO LOAD WASM FILES
ort.env.wasm.wasmPaths = "wasm/";
// Load the model and create InferenceSession
const modelPathE = "/models/image_encoder_hiera_t.onnx";
const modelPath = "/models/mask_decoder_hiera_t.onnx";
const modelPath1 = "/models/memory_attention_hiera_t.onnx";
const modelPath2 = "/models/memory_encoder_hiera_t.onnx";
const modelPath3 = "/models/mlp_hiera_t.onnx";
const modelPath4 = "/models/prompt_encoder_hiera_t.onnx";
const mask_decoder_hiera_t = await ort.InferenceSession.create(
modelPath,
{
executionProviders: ["wasm"],
},
);
console.log(mask_decoder_hiera_t);
const memory_attention_hiera_t = await ort.InferenceSession.create(
modelPath1,
{
executionProviders: ["wasm"],
},
);
console.log(memory_attention_hiera_t);
const memory_encoder_hiera_t = await ort.InferenceSession.create(
modelPath2,
{
executionProviders: ["wasm"],
},
);
console.log(memory_encoder_hiera_t);
const mlp_hiera_t = await ort.InferenceSession.create(modelPath3, {
executionProviders: ["wasm"],
});
console.log(mlp_hiera_t);
const prompt_encoder_hiera_t = await ort.InferenceSession.create(
modelPath4,
{
executionProviders: ["wasm"],
},
);
console.log(prompt_encoder_hiera_t);
// // Run inference
// const outputs = await session.run({ input: inputTensor });
// console.log(outputs);
} catch (err) {
console.error("error caught: ", err);
}
}
// Sandbox is quickly a way to test your idea.
public async sandbox() {}
@@ -311,11 +251,56 @@ export class Engine {
// load canvas that was originaly saved TODO Save manager for resharing.
uiAccess.toolbarImage.hide();
uiAccess.loadingBar.hide();
this.setupStage();
}
public isInitialized() {
return this.stage !== null;
}
public async populateWithDebugItems() {
const imageFile = await FileUtilities.createImageFileFromUrl(
"https://static.miraheze.org/pgrwiki/0/0d/Dialogue-2B-Icon.png",
);
const imageNode = new ImageNode(
this.videoLayer,
this.renderEngine.captureCanvas.position().x,
this.renderEngine.captureCanvas.position().y,
imageFile,
this.selectionManager,
);
const imageNode2 = new ImageNode(
this.videoLayer,
this.renderEngine.captureCanvas.position().x,
this.renderEngine.captureCanvas.position().y,
imageFile,
this.selectionManager,
);
// Adding nodes here
const videoNode = new VideoNode(
this.videoLayer,
this.renderEngine.captureCanvas.position().x,
this.renderEngine.captureCanvas.position().y,
"https://storage.googleapis.com/vocodes-public/media/r/q/p/r/e/rqpret6mkh18dqwjqwghhdqf15x720s1/storyteller_rqpret6mkh18dqwjqwghhdqf15x720s1.mp4",
this.selectionManager,
);
// CODE TO TEST RENDER ENGINE
// Testing render engine
// this.renderEngine.addNodes(videoNode);
// await this.renderEngine.startProcessing();
//videoNode.simulatedLoading();
// TODO support Text nodes
this.renderEngine.addNodes(videoNode);
this.renderEngine.addNodes(imageNode2);
// TODO if only image take image and just takes snapshots Edge case
this.renderEngine.addNodes(imageNode);
}
public async setupStage() {
var textNode = new Konva.Text({
x: 0,
@@ -339,23 +324,6 @@ export class Engine {
}, this.videoLayer);
anim.start();
// Adding nodes here
// const videoNode = new VideoNode(
// "",
// this.offScreenCanvas,
// this.videoLayer,
// 1560,
// 400,
// "https://storage.googleapis.com/vocodes-public/media/r/q/p/r/e/rqpret6mkh18dqwjqwghhdqf15x720s1/storyteller_rqpret6mkh18dqwjqwghhdqf15x720s1.mp4",
// this.selectionManager,
// );
// CODE TO TEST RENDER ENGINE
// Testing render engine
// this.renderEngine.addNodes(videoNode);
// await this.renderEngine.startProcessing();
//videoNode.simulatedLoading();
// TODO support Text nodes
this.videoLayer.add(textNode);
this.addKeyboardShortcuts();
@@ -363,39 +331,39 @@ export class Engine {
public addImage(imageFile: File) {
const imageNode = new ImageNode(
uuidv4(),
this.videoLayer,
50,
50,
this.renderEngine.captureCanvas.position().x,
this.renderEngine.captureCanvas.position().y,
imageFile,
this.selectionManager,
);
this.renderEngine.addNodes(imageNode);
}
public addVideo(url: string) {
// Adding nodes here
const videoNode = new VideoNode(
uuidv4(),
this.videoLayer,
1560,
400,
this.renderEngine.captureCanvas.position().x,
this.renderEngine.captureCanvas.position().y,
url,
this.selectionManager,
undefined,
undefined,
);
this.renderEngine.addNodes(videoNode);
}
// Events for Undo and Redo
private addKeyboardShortcuts() {
window.addEventListener("keydown", (event) => {
if (event.ctrlKey && event.key === "z") {
this.undo();
console.log("Undo");
this.undoStackManager.undo();
} else if (
(event.ctrlKey && event.key === "y") ||
(event.ctrlKey && event.shiftKey && event.key === "Z")
) {
this.redo();
console.log("Redo");
this.undoStackManager.redo();
}
});
}
@@ -410,26 +378,18 @@ export class Engine {
this.undoStackManager.executeCommand(command);
}
scaleNodes(nodes: Konva.Node[], newScaleX: number, newScaleY: number) {
const command = new ScaleCommand(nodes, newScaleX, newScaleY);
this.undoStackManager.executeCommand(command);
}
deleteNodes(nodes: Konva.Node[]) {
const command = new DeleteCommand(nodes);
this.undoStackManager.executeCommand(command);
}
createNodes(nodes: Konva.Node[]) {
const command = new CreateCommand(nodes, this.videoLayer);
const command = new CreateCommand(nodes);
this.undoStackManager.executeCommand(command);
}
scaleNodes(nodes: Konva.Node[], newScaleX: number, newScaleY: number) {
const command = new ScaleCommand(nodes, newScaleX, newScaleY);
this.undoStackManager.executeCommand(command);
}
undo() {
this.undoStackManager.undo();
}
redo() {
this.undoStackManager.redo();
}
}
@@ -35,4 +35,14 @@ export class FileUtilities {
console.log(error);
}
}
static async createImageFileFromUrl(
url: string,
fileName: string = "image.jpg",
mimeType: string = "image/jpeg",
): Promise<File> {
const response = await fetch(url);
const blob = await response.blob();
return new File([blob], fileName, { type: mimeType });
}
}
+39 -152
View File
@@ -1,185 +1,72 @@
import Konva from "konva";
import { Layer } from "konva/lib/Layer";
import { NetworkedNodeContext } from "./NetworkedNodeContext";
import { v4 as uuidv4 } from "uuid";
import { uiAccess } from "~/signals";
import { SelectionManager } from "../SelectionManager";
const toolbarVideo = uiAccess.toolbarImage;
const loadingBar = uiAccess.loadingBar;
export class ImageNode extends NetworkedNodeContext {
public selectionManagerRef: SelectionManager;
public imageURL: string;
public videoComponent: HTMLVideoElement;
public kNode: Konva.Image;
private imageObject: HTMLImageElement;
// do not modify internal
public didFinishLoading: boolean;
private videoLayer: Layer;
public uuid: string;
// This locks interaction when the render engine is rendering
private isProcessing: boolean = false;
async setProcessing() {
this.isProcessing = true;
}
public delete() {
// Do any other clean up and delete the konva node.
if (this.kNode) {
this.kNode.destroy();
}
}
public highlight() {
this.kNode.stroke("salmon");
this.kNode.strokeWidth(12);
this.kNode.draw();
}
public unHighLight() {
this.kNode.stroke(null);
this.kNode.strokeWidth(0);
this.kNode.draw();
}
private imageObject: Image;
constructor(
uuid: string = uuidv4(),
videoLayer: Layer,
mediaLayer: Layer,
x: number,
y: number,
imageFile: File,
selectionManagerRef: SelectionManager,
) {
super();
super(selectionManagerRef, mediaLayer);
this.selectionManagerRef = selectionManagerRef;
this.uuid = uuid;
this.videoLayer = videoLayer;
this.mediaLayer = mediaLayer;
this.didFinishLoading = false;
const imageObj = new Image();
this.imageObject = imageObj;
const imageComponent = new Image();
this.imageObject = imageComponent;
this.imageObject.crossOrigin = "anonymous";
imageObj.onload = () => {
// TODO load this proper.
this.kNode = new Konva.Image({
x: x,
y: y,
image: imageObj,
width: imageObj.width,
height: imageObj.height,
draggable: true,
});
// TODO use loading image
this.kNode = new Konva.Image({
x: x,
y: y,
image: undefined, // to do replace with placeholder
width: 100,
height: 100,
fill: "gray",
draggable: true,
});
this.mediaLayer.add(this.kNode);
imageComponent.onload = () => {
if (!this.kNode) {
return;
}
this.kNode.image(imageComponent);
this.kNode.width(imageComponent.width);
this.kNode.height(imageComponent.height);
this.listenToBaseKNode();
this.kNode.fill(null);
this.mediaLayer.draw();
this.didFinishLoading = true;
// add the shape to the layer
this.kNode.on("dragstart", (e) => {
this.updateContextMenuPosition();
// Multiselect
const isMultiSelect = e.evt.shiftKey;
this.selectionManagerRef.startDrag(this);
if (this.didFinishLoading == false) {
this.updateLoadingBarPosition();
}
});
this.kNode.on("dragmove", (e) => {
// shouldn't be able to move if processing.
this.updateContextMenuPosition();
// Multiselect
const isMultiSelect = e.evt.shiftKey;
this.selectionManagerRef.dragging(this);
if (this.isProcessing) {
return;
}
if (this.didFinishLoading == false) {
this.updateLoadingBarPosition();
}
});
this.kNode.on("dragend", (e) => {
this.selectionManagerRef.draggingStopped(this);
});
this.kNode.on("dragend", () => {
this.updateContextMenuPosition();
if (this.didFinishLoading == false) {
this.updateLoadingBarPosition();
}
});
this.kNode.on("mousedown", (e) => {
toolbarVideo.show();
// selection on click doesn't do a good job.
const isMultiSelect = e.evt.shiftKey;
this.selectionManagerRef.selectNode(this, isMultiSelect);
this.updateContextMenuPosition();
if (this.didFinishLoading == false) {
this.updateLoadingBarPosition();
} else {
loadingBar.hide();
}
});
this.kNode.on("mouseup", () => {
if (this.didFinishLoading == false) {
return;
}
});
videoLayer.add(this.kNode);
videoLayer.draw();
};
imageObj.src = URL.createObjectURL(imageFile);
imageComponent.src = URL.createObjectURL(imageFile);
}
async updateImage(newImageSrc: string) {
const newImage = new Image();
newImage.src = newImageSrc;
newImage.onload = () => {
if (!this.kNode) {
return;
}
this.kNode.image(newImage);
this.kNode.width(newImage.width);
this.kNode.height(newImage.height);
this.kNode.fill(null);
this.kNode.draw();
};
}
updateContextMenuPosition() {
toolbarVideo.setPosition({
x: this.kNode.getPosition().x + this.kNode.getSize().width / 4,
y: this.kNode.getPosition().y - 150,
});
}
updateLoadingBarPosition() {
loadingBar.updatePosition({
x: this.kNode.getPosition().x + this.kNode.getSize().width / 4,
y: this.kNode.getPosition().y - 60,
});
}
public bringToFront() {
this.kNode.moveUp();
}
public sendBack() {
// prevent canvas being in front.
if (this.kNode.zIndex() === 1) {
return;
}
this.kNode.moveDown();
}
}
+191 -20
View File
@@ -1,27 +1,198 @@
// This class takes a url and then loads it async not blocking
// This also handles the UI
import { uiAccess } from "~/signals";
import { SelectionManager } from "../SelectionManager";
import Konva from "konva";
import { Layer } from "konva/lib/Layer";
import { v4 as uuidv4 } from "uuid";
const toolbarVideo = uiAccess.toolbarImage;
const loadingBar = uiAccess.loadingBar;
export class NetworkedNodeContext {
constructor() {}
public selectionManagerRef: SelectionManager;
public kNode: Konva.Image | undefined;
protected mediaLayer: Layer;
public uuid: string;
async startLoading(): Promise<void> {}
async endLoading(): Promise<void> {}
async progressiveLoading(currentAmount: number): Promise<void> {}
async updateCanvasElement() {}
// Internal State members
// do not modify internal
public didFinishLoading: boolean = false;
// This locks interaction when the render engine is rendering
protected isProcessing: boolean = false;
// Debug outputs
async blobToFile(blob: Blob) {
try {
const link = document.createElement("a");
link.href = URL.createObjectURL(blob);
link.download = "canvas-output.jpg";
// Trigger the download
link.click();
// Clean up the URL object
URL.revokeObjectURL(link.href);
console.log("Downloaded Sample");
} catch (error) {
console.log(error);
async setProcessing() {
this.isProcessing = true;
}
constructor(selectionManagerRef: SelectionManager, mediaLayer: Layer) {
this.uuid = uuidv4();
this.mediaLayer = mediaLayer;
this.selectionManagerRef = selectionManagerRef;
this.kNode = undefined;
this.didFinishLoading = false;
}
public delete() {
// Do any other clean up and delete the konva node.
if (this.kNode) {
this.kNode.destroy();
}
}
public highlight() {
if (!this.kNode) {
return;
}
this.kNode.stroke("salmon");
this.kNode.strokeWidth(10);
this.kNode.draw();
}
public unHighLight() {
if (!this.kNode) {
return;
}
this.kNode.stroke(null);
this.kNode.strokeWidth(0);
this.kNode.draw();
}
updateContextMenuPosition() {
if (!this.kNode) {
return;
}
toolbarVideo.setPosition({
x: this.kNode.getPosition().x + this.kNode.getSize().width / 4,
y: this.kNode.getPosition().y - 150,
});
}
updateLoadingBarPosition() {
if (!this.kNode) {
return;
}
loadingBar.updatePosition({
x: this.kNode.getPosition().x + this.kNode.getSize().width / 4,
y: this.kNode.getPosition().y - 60,
});
}
public bringToFront() {
if (!this.kNode) {
return;
}
this.kNode.moveUp();
}
public sendBack() {
if (!this.kNode) {
return;
}
// prevent canvas being in front.
if (this.kNode.zIndex() === 1) {
return;
}
this.kNode.moveDown();
}
public async updateContextMenu() {
this.updateContextMenuPosition();
this.updateLoadingBarPosition();
}
public listenToBaseKNode() {
if (!this.kNode) {
return;
}
this.kNode.on("dragstart", (e) => {
// console.log("Drag start");
this.updateContextMenuPosition();
// Multiselect
const isMultiSelect = e.evt.shiftKey;
if (isMultiSelect) {
this.selectionManagerRef.startDrag(this);
}
if (this.didFinishLoading == false) {
this.updateLoadingBarPosition();
}
});
this.kNode.on("dragmove", (e) => {
// shouldn't be able to move if processing.
// console.log("Drag Move");
this.updateContextMenuPosition();
// Multiselect
const isMultiSelect = e.evt.shiftKey;
if (isMultiSelect) {
this.selectionManagerRef.dragging(this);
}
if (this.isProcessing) {
return;
}
if (this.didFinishLoading == false) {
this.updateLoadingBarPosition();
}
});
this.kNode.on("dragend", (e) => {
// console.log("Drag End");
const isMultiSelect = e.evt.shiftKey;
if (isMultiSelect) {
this.selectionManagerRef.draggingStopped(this);
}
this.updateContextMenuPosition();
if (this.didFinishLoading == false) {
this.updateLoadingBarPosition();
}
});
this.kNode.on("click", (e) => {
console.log("click");
const isMultiSelect = e.evt.shiftKey;
if (isMultiSelect) {
if (this.selectionManagerRef.isNodeSelected(this)) {
this.selectionManagerRef.deselectNode(this);
} else {
this.selectionManagerRef.selectNode(this);
}
}
});
this.kNode.on("mousedown", (e) => {
toolbarVideo.show();
console.log("Mouse Down");
const isMultiSelect = e.evt.shiftKey;
if (isMultiSelect) {
console.log("Shift");
} else {
console.log("No Shift");
this.selectionManagerRef.clearSelection();
this.selectionManagerRef.selectNode(this);
}
// selection on click doesn't do a good job.
this.updateContextMenuPosition();
if (this.didFinishLoading == false) {
this.updateLoadingBarPosition();
} else {
loadingBar.hide();
}
});
this.kNode.on("mouseup", (e) => {
if (this.didFinishLoading == false) {
return;
}
});
}
}
+74 -219
View File
@@ -1,66 +1,29 @@
import Konva from "konva";
import { Layer } from "konva/lib/Layer";
import { NetworkedNodeContext } from "./NetworkedNodeContext";
import { v4 as uuidv4 } from "uuid";
import { uiAccess } from "~/signals";
import { SelectionManager } from "../SelectionManager";
const toolbarVideo = uiAccess.toolbarImage;
const loadingBar = uiAccess.loadingBar;
export class VideoNode extends NetworkedNodeContext {
public selectionManagerRef: SelectionManager | undefined;
public videoURL: string;
public videoComponent: HTMLVideoElement;
public kNode: Konva.Image;
// do not modify internal
public didFinishLoading: boolean;
private videoLayer: Layer;
public uuid: string;
private shouldPlay: boolean = true;
// Use Context Menu Item
public duration: number = 0;
private imageIndex: number = 0;
public fps: number = 24;
// This locks interaction when the render engine is rendering
private isProcessing: boolean = false;
async setProcessing() {
this.isProcessing = true;
}
private finishedLoadingOnStart: Promise<void>;
public delete() {
// Do any other clean up and delete the konva node.
if (this.kNode) {
this.kNode.destroy();
}
}
getNumberFrames(): number {
return this.fps * this.duration;
}
public highlight() {
this.kNode.stroke("salmon");
this.kNode.strokeWidth(12);
this.kNode.draw();
}
public unHighLight() {
this.kNode.stroke(null);
this.kNode.strokeWidth(0);
this.kNode.draw();
}
private imageSources: string[] = [
"https://images-ng.pixai.art/images/orig/7ef23baa-2fc8-4e2f-8299-4f9241920090",
"https://images-ng.pixai.art/images/orig/98196e9f-b968-4fe1-97ec-083ffd77c263",
@@ -71,34 +34,27 @@ export class VideoNode extends NetworkedNodeContext {
"https://images-ng.pixai.art/images/orig/56dcbb5f-7a31-4328-b4ea-1312df6e77a0",
"https://videos.pixai.art/f7df019d-79a2-4ed2-bb99-775c941f7ec6",
];
private frameDidFinishSeeking: Promise<void>;
private finishedLoadingOnStart: Promise<void>;
constructor(
uuid: string = uuidv4(),
videoLayer: Layer,
x: number,
y: number,
videoURL: string,
selectionManagerRef: SelectionManager | undefined,
width: number | undefined,
height: number | undefined,
selectionManagerRef: SelectionManager,
) {
super();
this.selectionManagerRef = selectionManagerRef;
this.shouldPlay = true; // start
this.uuid = uuid;
this.videoLayer = videoLayer;
// state manage the node
super(selectionManagerRef, videoLayer);
// state manage the node
// use web codecs to get the frame rate 89% support
// assume 60fps for now.
this.fps = 24; // need to query this from the media
this.duration = -1;
this.didFinishLoading = false;
this.duration = -1; // video duration
this.videoURL = videoURL;
this.videoComponent = document.createElement("video");
this.videoComponent.crossOrigin = "anonymous";
@@ -107,33 +63,49 @@ export class VideoNode extends NetworkedNodeContext {
this.finishedLoadingOnStart = new Promise<void>(() => {});
this.kNode = new Konva.Image({
image: this.videoComponent,
image: undefined,
x: x,
y: y,
width: 200, // to do fix this with placeholder
height: 200,
draggable: true,
fill: "grey",
});
this.mediaLayer.add(this.kNode);
if (this.selectionManagerRef === undefined) {
this.kNode.listening(false);
}
this.videoComponent.onloadstart = (event: Event) => {
loadingBar.show();
loadingBar.updateProgress(0);
};
this.videoComponent.onloadedmetadata = (event: Event) => {
loadingBar.updateProgress(25);
console.log("Loaded Metadata");
if (height) {
this.kNode.height(height);
} else {
this.kNode.height(this.videoComponent.videoHeight);
}
if (width) {
this.kNode.width(width);
} else {
this.kNode.width(this.videoComponent.videoWidth);
if (!this.kNode) {
console.log("KNode Not Initialized Video Component");
return;
}
this.kNode.height(this.videoComponent.videoHeight);
this.kNode.width(this.videoComponent.videoWidth);
this.kNode.image(this.videoComponent);
this.videoComponent.currentTime = 0; // ensure it shows up on screen
// it might have length here which we will need to trim down to 7 seconds.
console.log(`Video Duration: ${this.videoComponent.duration}`);
this.duration = this.videoComponent.duration;
this.kNode.fill(null);
loadingBar.updateProgress(50);
};
this.videoComponent.onerror = (
event: Event | string,
source?: string,
lineno?: number,
colno?: number,
error?: Error,
) => {
loadingBar.hide();
loadingBar.updateProgress(0);
console.error("Error loading video:", event);
};
this.finishedLoadingOnStart = new Promise<void>((resolve, reject) => {
@@ -141,6 +113,8 @@ export class VideoNode extends NetworkedNodeContext {
this.didFinishLoading = true;
// Might have to auto click? on first load this doesn't work in general how about after ?
this.videoComponent.play(); //sometimes race condition with the
loadingBar.updateProgress(100);
loadingBar.hide();
resolve();
};
});
@@ -148,129 +122,53 @@ export class VideoNode extends NetworkedNodeContext {
// assign video to start process.
this.videoComponent.src = this.videoURL;
this.videoLayer.add(this.kNode);
this.kNode.on("dragstart", (e) => {
this.updateContextMenuPosition();
// Multiselect
const isMultiSelect = e.evt.shiftKey;
if (this.selectionManagerRef) {
this.selectionManagerRef.startDrag(this);
}
if (this.didFinishLoading == false) {
this.updateLoadingBarPosition();
}
});
this.kNode.on("dragmove", (e) => {
// shouldn't be able to move if processing.
this.updateContextMenuPosition();
// Multiselect
const isMultiSelect = e.evt.shiftKey;
if (this.selectionManagerRef) {
this.selectionManagerRef.dragging(this);
}
if (this.isProcessing) {
return;
}
if (this.didFinishLoading == false) {
this.updateLoadingBarPosition();
}
});
this.kNode.on("dragend", (e) => {
if (this.selectionManagerRef) {
this.selectionManagerRef.draggingStopped(this);
}
});
this.kNode.on("mouseover", () => {
//this.highlight();
});
this.kNode.on("mouseout", () => {
// this.unHighLight();
});
this.kNode.on("dragend", () => {
this.updateContextMenuPosition();
if (this.didFinishLoading == false) {
this.updateLoadingBarPosition();
}
});
this.kNode.on("mousedown", (e) => {
toolbarVideo.show();
// selection on click doesn't do a good job.
const isMultiSelect = e.evt.shiftKey;
if (this.selectionManagerRef) {
this.selectionManagerRef.selectNode(this, isMultiSelect);
}
this.updateContextMenuPosition();
if (this.didFinishLoading == false) {
this.updateLoadingBarPosition();
} else {
loadingBar.hide();
}
});
this.listenToBaseKNode();
// only for video
this.kNode.on("mouseup", () => {
console.log("Mouse Up");
// Shouldn't play if anything things are true
if (this.didFinishLoading == false) {
return;
}
this.play();
if (this.isProcessing == true) {
return;
}
if (this.videoComponent.paused) {
console.log("Playing");
this.videoComponent.play();
} else {
console.log("Pause");
this.videoComponent.pause();
}
});
}
stop() {
if (this.shouldPlay == false) {
this.shouldPlay = true;
this.videoComponent.pause();
}
async reset() {
this.videoComponent.pause();
await this.seek(0);
}
async updateImage(newImageSrc: string) {
const newImage = new Image();
newImage.src = newImageSrc;
newImage.onload = () => {
if (!this.kNode) {
console.log("selectedNode KNode is initialized");
return;
}
this.kNode.image(newImage);
this.kNode.draw();
};
}
updateContextMenuPosition() {
toolbarVideo.setPosition({
x: this.kNode.getPosition().x + this.kNode.getSize().width / 4,
y: this.kNode.getPosition().y - 150,
});
}
updateLoadingBarPosition() {
loadingBar.updatePosition({
x: this.kNode.getPosition().x + this.kNode.getSize().width / 4,
y: this.kNode.getPosition().y - 60,
});
}
public bringToFront() {
this.kNode.moveUp();
}
public sendBack() {
// prevent canvas being in front.
if (this.kNode.zIndex() === 1) {
return;
}
this.kNode.moveDown();
}
public async createVideoElement(newURL: string) {
// try catch here with a retry button.
if (!this.kNode) {
console.log("selectedNode KNode is initialized");
return;
}
this.kNode.fill("grey"); // todo fix with loader
const videoComponent = document.createElement("video");
videoComponent.crossOrigin = "anonymous";
@@ -278,6 +176,7 @@ export class VideoNode extends NetworkedNodeContext {
videoComponent.src = newURL;
console.log("Video Data");
console.log(newURL);
videoComponent.onloadstart = (event: Event) => {
this.didFinishLoading = false;
console.log("OnLoadStart");
@@ -285,64 +184,21 @@ export class VideoNode extends NetworkedNodeContext {
videoComponent.onloadedmetadata = (event: Event) => {
console.log("Loaded Metadata");
if (!this.kNode) {
console.log("selectedNode KNode is initialized");
return;
}
this.kNode.width(videoComponent.videoWidth);
this.kNode.height(videoComponent.videoHeight);
this.kNode.image(videoComponent);
// it might have length here which we will need to trim down to 7 seconds.
console.log(`Video Duration: ${videoComponent.duration}`);
videoComponent.currentTime = 0; // ensure it shows up on screen
this.duration = videoComponent.duration;
this.videoComponent = videoComponent;
};
// videoComponent.onloadeddata = (event: Event) => {
// try {
// console.log("LoadedData");
// this.kNode.image(videoComponent);
// this.kNode.draw();
// this.videoComponent.loop = true;
// this.videoComponent.currentTime = 0; // ensure it shows up on screen
// this.shouldPlay = true; // means its play state
// } catch (error) {
// console.log(error);
// }
// };
// videoComponent.oncanplaythrough = (event: Event) => {
// // remove loading ui
// loadingBar.updateProgress(100);
// loadingBar.hide();
// this.didFinishLoading = true;
// // Might have to auto click? on first load this doesn't work in general how about after ?
// //this.play(); //sometimes race condition with the
// };
}
private play() {
try {
if (this.didFinishLoading === false || this.isProcessing) {
return;
}
//console.log(`${this.didFinishLoading} ${this.shouldPlay}`);
if (this.didFinishLoading && this.shouldPlay === true) {
//console.log("Playing");
this.shouldPlay = false;
this.videoComponent.play();
} else {
this.shouldPlay = true;
this.videoComponent.pause();
//console.log("Pausing");
}
} catch (error) {
//console.log(error);
}
}
async simulatedLoading() {
// need to block playing while loading
this.didFinishLoading = false;
@@ -403,7 +259,6 @@ export class VideoNode extends NetworkedNodeContext {
resolve();
};
});
// await
await this.frameDidFinishSeeking;
} else {
console.log("Video Not Seekable");
+53
View File
@@ -0,0 +1,53 @@
// public async onnx() {
// try {
// //DO NOT REMOVE, NECESSARY TO LOAD WASM FILES
// ort.env.wasm.wasmPaths = "wasm/";
// // Load the model and create InferenceSession
// const modelPathE = "/models/image_encoder_hiera_t.onnx";
// const modelPath = "/models/mask_decoder_hiera_t.onnx";
// const modelPath1 = "/models/memory_attention_hiera_t.onnx";
// const modelPath2 = "/models/memory_encoder_hiera_t.onnx";
// const modelPath3 = "/models/mlp_hiera_t.onnx";
// const modelPath4 = "/models/prompt_encoder_hiera_t.onnx";
// const mask_decoder_hiera_t = await ort.InferenceSession.create(
// modelPath,
// {
// executionProviders: ["wasm"],
// },
// );
// console.log(mask_decoder_hiera_t);
// const memory_attention_hiera_t = await ort.InferenceSession.create(
// modelPath1,
// {
// executionProviders: ["wasm"],
// },
// );
// console.log(memory_attention_hiera_t);
// const memory_encoder_hiera_t = await ort.InferenceSession.create(
// modelPath2,
// {
// executionProviders: ["wasm"],
// },
// );
// console.log(memory_encoder_hiera_t);
// const mlp_hiera_t = await ort.InferenceSession.create(modelPath3, {
// executionProviders: ["wasm"],
// });
// console.log(mlp_hiera_t);
// const prompt_encoder_hiera_t = await ort.InferenceSession.create(
// modelPath4,
// {
// executionProviders: ["wasm"],
// },
// );
// console.log(prompt_encoder_hiera_t);
// // // Run inference
// // const outputs = await session.run({ input: inputTensor });
// // console.log(outputs);
// } catch (err) {
// console.error("error caught: ", err);
// }
// } REFERENCE CODE.
@@ -0,0 +1,54 @@
import { ICanvasConfig } from "konva/lib/Canvas"; // have to export and modify the code TODO:/
import { Canvas } from "konva/lib/Canvas";
import { OffScreenSceneContext } from "./OffScreenSceneContext";
export class OffScreenSceneCanvas extends Canvas {
constructor(
config: ICanvasConfig = { width: 0, height: 0, willReadFrequently: false },
) {
super(config); // creates canvas then you have to replace it
// replace here also kills the css but thats ok
this._canvas = new OffscreenCanvas(config.width, config.height);
this.context = new OffScreenSceneContext(this, {
willReadFrequently: config.willReadFrequently,
});
this.setSize(config.width, config.height);
}
setPixelRatio(pixelRatio: number) {
console.log("overriden setPixelRatio");
var previousRatio = this.pixelRatio;
this.pixelRatio = pixelRatio;
this.setSize(
this.getWidth() / previousRatio,
this.getHeight() / previousRatio,
);
}
public override setWidth(width: number) {
// take into account pixel ratio
console.log("overriden setWidth");
this.width = this._canvas.width = width * this.pixelRatio;
var pixelRatio = this.pixelRatio,
_context = this.getContext()._context;
_context.scale(pixelRatio, pixelRatio);
}
public override setHeight(height: number) {
// take into account pixel ratio
console.log("overriden setHeight");
this.height = this._canvas.height = height * this.pixelRatio;
var pixelRatio = this.pixelRatio,
_context = this.getContext()._context;
_context.scale(pixelRatio, pixelRatio);
}
getWidth() {
return this.width;
}
getHeight() {
return this.height;
}
setSize(width: number | undefined, height: number | undefined) {
this.setWidth(width || 0);
this.setHeight(height || 0);
}
}
@@ -0,0 +1,11 @@
import { Context } from "konva/lib/Context";
import { Canvas } from "konva/lib/Canvas";
export class OffScreenSceneContext extends Context {
constructor(canvas: Canvas, { willReadFrequently = false } = {}) {
super(canvas);
this._context = canvas._canvas.getContext("2d", {
willReadFrequently,
}) as CanvasRenderingContext2D;
}
}
+141 -40
View File
@@ -1,7 +1,9 @@
import { VideoNode } from "../Nodes/VideoNode";
import Konva from "konva";
import { RenderTask } from "./RenderTask";
import { Container } from "konva/lib/Container";
import { Shape } from "konva/lib/Shape";
import { Group } from "konva/lib/Group";
// Hide these two to start. WIL
// uiAccess.toolbarMain.loadingBar.hide();
// uiEvents.toolbarMain.loadingBarRetry.onClick((e) => {
@@ -25,12 +27,15 @@ import {
} from "../SharedWorkers/Diffusion/DiffusionSharedWorker";
import { RenderingOptions } from "../Engine";
import { ImageNode } from "../Nodes/ImageNode";
import { FileUtilities } from "../FileUtilities/FileUtilities";
import { OffScreenSceneCanvas } from "./OffScreenSceneCanvas";
// https://www.aiseesoft.com/resource/phone-aspect-ratio-screen-resolution.html#:~:text=16%3A9%20Aspect%20Ratio
export class RenderEngine {
private videoNodes: VideoNode[];
private imageNodes: ImageNode[];
private offScreenCanvas: OffscreenCanvas;
private context: OffscreenCanvasRenderingContext2D | null;
@@ -83,6 +88,7 @@ export class RenderEngine {
if (height) {
this.height = height;
}
// Ensures that all the nodes stag in the same place should
// there be a window resize.
// recompute the position
@@ -132,6 +138,12 @@ export class RenderEngine {
y: pos.y + deltaY,
});
}
// update the context menu
this.videoNodes.forEach((node) => {
// find selected node... TODO and update that position
node.updateContextMenu();
});
this.videoLayer.batchDraw();
}
constructor(
@@ -217,8 +229,8 @@ export class RenderEngine {
if (typeof SharedWorker !== "undefined") {
console.log("Shared Workers are supported in this browser.");
// Debug chrome://inspect/#workers
// "src\\KonvaApp\\SharedWorkers\\Diffusion\\DiffusionSharedWorker.ts",
this.diffusionWorker = new DiffusionSharedWorkerClient(
"src\\KonvaApp\\SharedWorkers\\Diffusion\\DiffusionSharedWorker.ts",
this.onRenderingSystemMessageRecieved,
);
@@ -230,6 +242,26 @@ export class RenderEngine {
}
}
public placeDebugRect(
x: number,
y: number,
width: number,
height: number,
layer: Konva.Layer,
): void {
const rect = new Konva.Rect({
x: x,
y: y,
width: width, // Default width
height: height, // Default height
fill: "green",
draggable: false,
});
layer.add(rect);
layer.draw();
}
// This function uses a portion of the video layer to capture just the capture canvas.
// capture everything after seeking each video node.
renderPortionOfLayer(
@@ -291,7 +323,11 @@ export class RenderEngine {
var failed = false;
for (let i = 0; i < this.videoNodes.length; i++) {
const item = this.videoNodes[i];
if (!item.kNode) {
return;
}
item.kNode.listening(false);
item.unHighLight();
if (item.didFinishLoading == false) {
// error out and show error message
//this.startProcessing();
@@ -309,7 +345,7 @@ export class RenderEngine {
throw Error("Wait For Items to Finish Processing.");
}
this.videoNodes.forEach((item: VideoNode) => {});
//this.videoNodes.forEach((item: VideoNode) => {});
// find the longest video node
let numberOfFrames = this.findLongestVideoLength();
@@ -326,6 +362,9 @@ export class RenderEngine {
// enable all nodes again.
for (let i = 0; i < this.videoNodes.length; i++) {
const item = this.videoNodes[i];
if (!item.kNode) {
return;
}
item.kNode.listening(true);
}
}
@@ -340,6 +379,79 @@ export class RenderEngine {
return frameNumber / frameRate;
}
/**
*
* @param config
*/
private async renderFrame(config: {
layerOfInterest: Konva.Layer; // layer where the element that you want to clip lives.
// XY and height of a captureCanvas ( region of interest )
x?: number; // x position of the region of interest
y?: number; // y position of the region of interest
width?: number; // size of the region of interest
height?: number; // size of the region of interest
pixelRatio?: number; // higher means higher quality
mimeType?: string; // image/jpeg or image/png
quality?: number; // 1.0 is the best.
test: boolean; // true == blob else Image Bitmap
}): Promise<ImageBitmap | Blob> {
try {
const box = config.layerOfInterest.getClientRect();
const stage = config.layerOfInterest.getStage();
const x = config.x !== undefined ? config.x : Math.floor(box.x);
const y = config.y !== undefined ? config.y : Math.floor(box.y);
const pixelRatio = config.pixelRatio || 1;
const container = config.layerOfInterest as Container<Group | Shape>;
const offScreenSceneCanvas = new OffScreenSceneCanvas({
width:
config.width || Math.ceil(box.width) || (stage ? stage.width() : 0),
height:
config.height ||
Math.ceil(box.height) ||
(stage ? stage.height() : 0),
pixelRatio: pixelRatio,
});
const context = offScreenSceneCanvas.getContext();
const buffer = new OffScreenSceneCanvas({
width:
offScreenSceneCanvas.width / offScreenSceneCanvas.pixelRatio +
Math.abs(x),
height:
offScreenSceneCanvas.height / offScreenSceneCanvas.pixelRatio +
Math.abs(y),
pixelRatio: offScreenSceneCanvas.pixelRatio,
});
context.save();
if (x || y) {
context.translate(-1 * x, -1 * y);
}
container.drawScene(offScreenSceneCanvas, undefined, buffer);
// Not a type mistake ... DO NOT FIX
const offscreenCanvas = offScreenSceneCanvas._canvas as OffscreenCanvas;
let result = undefined;
if (config.test) {
const blob = await offscreenCanvas.convertToBlob({
quality: config.quality ?? 1.0,
type: "image/jpeg",
});
await FileUtilities.blobToFileJpeg(blob, "1");
result = blob;
} else {
result = offscreenCanvas.transferToImageBitmap();
}
context.restore();
return result;
} catch (error) {
throw error;
}
}
/**
find longest video
then seek through each node 1 step.
@@ -353,13 +465,12 @@ export class RenderEngine {
// Stop all nodes first
console.log(`LargestNumberOfFrames:${largestNumberOfFrames}`);
for (let k = 0; k < this.videoNodes.length; k++) {
const videoNode = this.videoNodes[k];
if (videoNode.didFinishLoading === false) {
throw Error("Videos Did Not Finish Loading Please Try Again.");
}
videoNode.stop();
await videoNode.reset();
}
// only pick nodes that intersect with the canvas on screen bounds to freeze.
@@ -401,14 +512,14 @@ export class RenderEngine {
this.width,
this.height,
);
// TODO write the non webworker version
//TODO write the non webworker version
const blob = await this.offScreenCanvas.convertToBlob({
quality: 1.0,
type: "image/jpeg",
});
await FileUtilities.blobToFileJpeg(blob, "1");
// const blob = await this.offScreenCanvas.convertToBlob({
// quality: 1.0,
// type: "image/jpeg",
// });
//await this.blobToFile(blob, `${j}`);
break;
} // end of for each frame
} else {
// decode on the shared webworker.
@@ -422,44 +533,32 @@ export class RenderEngine {
return;
}
this.context.drawImage(
this.videoLayer.canvas._canvas,
this.positionX,
this.positionY,
this.width,
this.height,
0,
0,
this.width,
this.height,
console.log(
`context: x:${this.positionX} y:${this.positionY} ${this.width} x ${this.height}`,
);
const bitmap = await this.renderFrame({
layerOfInterest: this.videoLayer,
x: this.captureCanvas.x(),
y: this.captureCanvas.y(),
width: this.width,
height: this.height,
mimeType: "image/jpeg",
pixelRatio: 1,
quality: 1.0,
test: false,
});
const data: DiffusionSharedWorkerItemData = {
height: this.height,
width: this.width,
imageBitmap: this.offScreenCanvas.transferToImageBitmap(),
imageBitmap: bitmap as ImageBitmap,
frame: j,
totalFrames: largestNumberOfFrames,
prompt: renderingOptions,
};
// let isDoneStreaming = false;
console.log(`Processing Frame:${j} out of ${largestNumberOfFrames}`);
// if (j == Math.floor(largestNumberOfFrames)) {
// isDoneStreaming = true;
// }
//console.log(isDoneStreaming);
this.diffusionWorker.sendData(1, data, false);
// // To ensure there is no lingering frames.
// if (isDoneStreaming) {
// console.log("Done streaming");
// break;
// }
}
} // end of largest number of frames loop
@@ -468,8 +567,10 @@ export class RenderEngine {
console.log("Didn't Initialize Diffusion");
return;
}
const jobID = 1;
this.diffusionWorker.sendData(
1,
jobID,
{
height: this.height,
width: this.width,
+34 -12
View File
@@ -1,7 +1,8 @@
import Konva from "konva";
import { VideoNode } from "./Nodes/VideoNode";
import { ImageNode } from "./Nodes/ImageNode";
type MediaNode = VideoNode | ImageNode;
import { NetworkedNodeContext } from "./Nodes/NetworkedNodeContext";
type MediaNode = NetworkedNodeContext | VideoNode | ImageNode;
export class SelectionManager {
private selectedNodes: Set<MediaNode>;
private initialPositions: Map<MediaNode, { x: number; y: number }>;
@@ -11,18 +12,18 @@ export class SelectionManager {
this.initialPositions = new Map<MediaNode, { x: number; y: number }>();
}
public selectNode(node: MediaNode, isMultiSelect: boolean): void {
if (!isMultiSelect) {
this.clearSelection();
}
public selectNode(node: MediaNode): void {
if (this.selectedNodes.has(node)) {
this.deselectNode(node);
return;
} else {
this.selectedNodes.add(node);
node.highlight();
}
if (!node.kNode) {
console.log("KNode is initialized");
return;
}
node.kNode.getLayer()?.batchDraw();
console.log("Selected Nodes:");
@@ -31,6 +32,11 @@ export class SelectionManager {
public deselectNode(node: MediaNode): void {
this.selectedNodes.delete(node);
node.unHighLight();
if (!node.kNode) {
console.log("KNode is initialized");
return;
}
node.kNode.getLayer()?.batchDraw();
}
@@ -44,19 +50,27 @@ export class SelectionManager {
}
// Start State
public startDrag(node: MediaNode | ImageNode): void {
public startDrag(node: MediaNode): void {
console.log("Starting Drag");
const position = node.kNode.position();
if (!node.kNode) {
console.log("KNode is initialized");
return;
}
this.selectedNodes.forEach((selectedNode) => {
if (!selectedNode.kNode) {
console.log("selectedNode KNode is initialized");
return;
}
this.initialPositions.set(selectedNode, {
x: selectedNode.kNode.x(),
y: selectedNode.kNode.y(),
});
});
}
public isNodeSelected(node: MediaNode): boolean {
return this.selectedNodes.has(node);
}
// Drag State
public dragging(node: MediaNode): void {
const initialPosition = this.initialPositions.get(node);
@@ -64,7 +78,10 @@ export class SelectionManager {
console.log("Initial Position Undefined");
return;
}
if (!node.kNode) {
console.log("selectedNode KNode is initialized");
return;
}
const dx = node.kNode.x() - initialPosition.x;
const dy = node.kNode.y() - initialPosition.y;
@@ -77,6 +94,11 @@ export class SelectionManager {
console.log("Initial Position is Null");
return;
}
if (!selectedNode.kNode) {
console.log("selectedNode KNode is initialized");
return;
}
selectedNode.kNode.x(initialPosition.x + dx);
selectedNode.kNode.y(initialPosition.y + dy);
});
@@ -24,6 +24,7 @@ export interface DiffusionSharedWorkerProgressData {
url: string;
status: JobStatus;
progress: number;
mediaToken: string;
}
export interface DiffusionSharedWorkerItemData {
@@ -137,7 +138,9 @@ export class DiffusionSharedWorker extends SharedWorkerBase<
url: "",
status: JobStatus.PENDING,
progress: (item.frame / aproxSteps / 2 / totalPercent) * 100,
mediaToken: "",
};
console.log("Lets report progress.");
reportProgress(progressData); // once finished gives you up to 50%
@@ -150,6 +153,17 @@ export class DiffusionSharedWorker extends SharedWorkerBase<
console.log(zipBlob);
console.log("Zipped");
const progressMedia: DiffusionSharedWorkerProgressData = {
url: "",
status: JobStatus.PENDING,
progress: (item.frame / aproxSteps / 2 / totalPercent) * 100,
mediaToken: "",
};
console.log("Lets report progress.");
reportProgress(progressMedia); // once finished gives you up to 50%
const response = await this.mediaAPI.UploadStudioShot({
maybe_title: "",
uuid_idempotency_token: uuidv4(),
@@ -222,6 +236,7 @@ export class DiffusionSharedWorker extends SharedWorkerBase<
url: "",
status: JobStatus.DEAD,
progress: computedProgress / totalPercent,
mediaToken: mediaToken,
};
renderProgressData.status = status;
@@ -254,7 +269,6 @@ export class DiffusionSharedWorker extends SharedWorkerBase<
jobIsProcessing = false;
reportProgress(renderProgressData);
throw Error("Server Failed to Process Please Try Again.");
break;
case JobStatus.CANCCELLED_BY_SYSTEM:
jobIsProcessing = false;
reportProgress(renderProgressData);
@@ -19,7 +19,6 @@ export class DiffusionSharedWorkerClient<
>,
) => void;
constructor(
workerPath: string,
messageReceived: (
response: SharedWorkerResponse<
DiffusionSharedWorkerResponseData,
@@ -28,43 +27,25 @@ export class DiffusionSharedWorkerClient<
) => void,
) {
this.messageReceived = messageReceived;
// if (import.meta.env.DEV) {
// console.log("This is running a worker in development");
// this.sharedWorker = new SharedWorker(workerPath, {
// type: "module",
// });
// } else {
try {
console.log("This is running a worker in production");
this.sharedWorker = new DiffusionSharedWorker();
// Uncomment this to test your CORS
// this.sharedWorker = new SharedWorker("worker.js", {
// type: "module",
// });
this.sharedWorker.addEventListener("error", (value) => {
console.log("Shared worker ERROR:");
console.log(value);
});
this.port = this.sharedWorker.port;
this.port.onmessage = this.onMessage.bind(this);
this.port.start();
console.log("launched shared worker (?)");
} catch (error) {
console.log("ERROR with shared worker!");
console.log(error);
throw Error("Could Not Start Worker");
}
//}
// // in production this is a work around .. https://github.com/vitejs/vite/issues/13680
// const js = `import ${JSON.stringify(new URL(diffusionWorkerURL, import.meta.url))}`;
// const blob = new Blob([js], { type: "application/javascript" });
// const objURL = URL.createObjectURL(blob);
// this.sharedWorker = new SharedWorker(new URL(objURL, import.meta.url), {
// type: "module",
// });
//}
this.port = this.sharedWorker.port;
this.port.onmessage = this.onMessage.bind(this);
this.port.start();
}
async onMessage(event: MessageEvent) {
@@ -91,7 +72,7 @@ export class DiffusionSharedWorkerClient<
async sendData(
jobID: number,
data: DiffusionSharedWorkerItemData | undefined,
data: DiffusionSharedWorkerItemData,
isDoneStreaming: boolean,
) {
const payload: SharedWorkerRequest<DiffusionSharedWorkerItemData> = {
+22
View File
@@ -0,0 +1,22 @@
import { ICommand } from "./ICommand";
import Konva from "konva";
export class MoveLayerDown implements ICommand {
private nodes: Konva.Node[];
private layer: Konva.Layer;
constructor(nodes: Konva.Node[]) {
this.nodes = nodes;
this.layer = nodes[0].getLayer()!;
}
execute() {
// this.nodes.forEach((node) => this.layer.add(node));
this.layer.draw();
}
undo() {
// this.nodes.forEach((node) => node.remove());
this.layer.draw();
}
}
+22
View File
@@ -0,0 +1,22 @@
import { ICommand } from "./ICommand";
import Konva from "konva";
export class MoveLayerUp implements ICommand {
private nodes: Konva.Node[];
private layer: Konva.Layer;
constructor(nodes: Konva.Node[]) {
this.nodes = nodes;
this.layer = nodes[0].getLayer()!;
}
execute() {
// this.nodes.forEach((node) => this.layer.add(node));
this.layer.draw();
}
undo() {
// this.nodes.forEach((node) => node.remove());
this.layer.draw();
}
}