Pass Dom References to Editor instead of Editor querying for DOM-IDs (#412)

- engine canvas is passed to editor as a DOM reference
- camera view canvas is passed to editor as a DOM reference
- scene container is passed to editor as a DOM reference
- amended all the editor code that wants to do a DOM query using DOM ID
to use the variable that stores the reference
- added guard functions to query the DOM-ID just incase the reference is
lost due to a re-render
- the above change guarantees the reference to the DOM, fixes the bug
where the editor cannot read the size of DOM elements, and increases
stability of the app in general.

Co-authored-by: wil <wil@storyteller.ai>
This commit is contained in:
wilandwil authored and GitHub committed 2024-05-22 11:39:39 -04:00
1 parent d69b368165
commit 7b5bdc915b
9 files changed
+207 -54

No files matched your search

+61 -24
View File
@@ -39,8 +39,12 @@ import {
import { updateObjectPanel } from "../signals";
import { GenerationOptions } from "../models/generationOptions";
import { toEngineActions } from "../Queue/toEngineActions";
export type EditorInitializeConfig = {
sceneToken: string;
editorCanvasEl: HTMLCanvasElement;
camViewCanvasEl: HTMLCanvasElement;
sceneContainerEl: HTMLDivElement;
};
class Editor {
@@ -51,8 +55,9 @@ class Editor {
renderer: THREE.WebGLRenderer | undefined;
rawRenderer: THREE.WebGLRenderer | undefined;
clock: THREE.Clock | undefined;
canvReference: HTMLCanvasElement | undefined = undefined;
canvasRenderCamReference: HTMLElement | undefined = undefined;
canvReference: HTMLCanvasElement | null = null;
canvasRenderCamReference: HTMLCanvasElement | null = null;
composer: EffectComposer | undefined;
outlinePass: OutlinePass | undefined;
last_cam_pos: THREE.Vector3;
@@ -229,7 +234,43 @@ class Editor {
return value === null || value.trim().length === 0;
}
initialize({ sceneToken }: EditorInitializeConfig) {
containerMayReset() {
if (!this.container) {
console.warn(
"Editor - Container does not exist, querying from DOM via document.getElementById",
);
this.container = document.getElementById("video-scene-container");
}
}
engineCanvasMayReset() {
if (!this.canvReference) {
console.warn(
"Editor - Engine Canbas does not exist, querying from DOM via document.getElementById",
);
this.canvReference = document.getElementById(
"video-scene",
) as HTMLCanvasElement;
}
}
camViewCanvasMayReset() {
if (!this.canvasRenderCamReference) {
console.warn(
"Editor - Cam View Canvas does not exist, querying from DOM via document.getElementById",
);
this.canvasRenderCamReference = document.getElementById(
"camera-view",
) as HTMLCanvasElement;
}
}
updateCamViewCanvas(newCanvas: HTMLCanvasElement) {
this.canvasRenderCamReference = newCanvas;
}
initialize({
sceneToken,
editorCanvasEl,
camViewCanvasEl,
sceneContainerEl,
}: EditorInitializeConfig) {
if (!this.can_initialize) {
console.log("Editor Already Initialized");
return;
@@ -237,21 +278,15 @@ class Editor {
this.can_initialize = false;
// Gets the canvas.
this.canvReference = (document.getElementById("video-scene") ??
undefined) as HTMLCanvasElement | undefined;
this.canvasRenderCamReference =
document.getElementById("camera-view") ?? undefined;
this.canvReference = editorCanvasEl;
this.canvasRenderCamReference = camViewCanvasEl;
// Find the container element
const container = document.getElementById("video-scene-container");
if (container == null) {
return;
}
this.container = sceneContainerEl;
// Use the container's dimensions
const width = container.offsetWidth;
const height = container.offsetHeight;
const width = this.container.offsetWidth;
const height = this.container.offsetHeight;
// Sets up camera and base position.
this.camera = new THREE.PerspectiveCamera(70, width / height, 0.01, 200);
@@ -674,9 +709,8 @@ class Editor {
1000 / (this.cap_fps * 2),
); // Get the most FPS we can out of the renderer.
if (this.container === undefined) {
this.container = document.getElementById("video-scene-container");
}
this.containerMayReset();
if (!this.rendering && this.container) {
if (
this.container.clientWidth + this.container.clientHeight !==
@@ -773,11 +807,14 @@ class Editor {
this.switchPreviewToggle = false;
editorState.value = EditorStates.EDIT;
setTimeout(() => {
this.canvasRenderCamReference =
document.getElementById("camera-view") ?? undefined;
// if (!this.canvasRenderCamReference) {
// this.canvasRenderCamReference =
// document.getElementById("camera-view");
// }
this.camViewCanvasMayReset();
this.rawRenderer = new THREE.WebGLRenderer({
antialias: false,
canvas: this.canvasRenderCamReference,
canvas: this.canvasRenderCamReference || undefined,
preserveDrawingBuffer: true,
});
if (this.camera_person_mode) {
@@ -889,7 +926,7 @@ class Editor {
// Automaticly resize scene.
onWindowResize() {
this.container = document.getElementById("video-scene-container");
this.containerMayReset();
if (!this.container) return;
const width = this.container.clientWidth;
@@ -918,8 +955,8 @@ class Editor {
}
setupResizeObserver() {
const container = document.getElementById("video-scene-container");
if (!container) {
this.containerMayReset();
if (!this.container) {
return;
}
@@ -935,7 +972,7 @@ class Editor {
}
});
resizeObserver.observe(container);
resizeObserver.observe(this.container);
}
getAssetType(selected: THREE.Object3D<THREE.Object3DEventMap>): AssetType {
@@ -1,5 +1,5 @@
import Editor from "./editor";
import { EditorStates } from "~/pages/PageEnigma/enums";
import { EditorStates, ClipType } from "~/pages/PageEnigma/enums";
import { editorState, previewSrc } from "../signals/engine";
import Queue from "~/pages/PageEnigma/Queue/Queue";
import { QueueNames } from "~/pages/PageEnigma/Queue/QueueNames";
@@ -7,7 +7,6 @@ import { fromEngineActions } from "~/pages/PageEnigma/Queue/fromEngineActions";
import { ToastTypes } from "~/enums";
import { createFFmpeg, fetchFile, FFmpeg } from "@ffmpeg/ffmpeg";
import { ClipUI } from "../datastructures/clips/clip_ui.js";
import { ClipType } from "~/pages/PageEnigma/enums";
import { Visibility } from "./api_manager.js";
import * as THREE from "three";
import { getSceneSignals } from "~/signals";
@@ -70,13 +69,17 @@ export class VideoGeneration {
previewSrc.value = url;
return Promise.resolve(url);
} catch (err: any) {
} catch (err: unknown) {
const errorMessage =
err instanceof Error
? err.message
: "Unknown Error in Generate Frame";
Queue.publish({
queueName: QueueNames.FROM_ENGINE,
action: fromEngineActions.POP_A_TOAST,
data: {
type: ToastTypes.ERROR,
message: err.message,
message: errorMessage,
},
});
}
@@ -259,7 +262,7 @@ export class VideoGeneration {
}
const upload_token = data["media_file_token"];
const result = await this.editor.api_manager
await this.editor.api_manager
.stylizeVideo(
upload_token,
this.editor.art_style,
@@ -287,11 +290,11 @@ export class VideoGeneration {
);
}
this.editor.canvasRenderCamReference =
document.getElementById("camera-view");
this.editor.camViewCanvasMayReset();
this.editor.rawRenderer = new THREE.WebGLRenderer({
antialias: false,
canvas: this.editor.canvasRenderCamReference,
canvas: this.editor.canvasRenderCamReference || undefined,
preserveDrawingBuffer: true,
});
this.editor.activeScene.renderMode(false);
+6 -13
View File
@@ -23,6 +23,9 @@ import {
editorLoader,
} from "~/pages/PageEnigma/signals";
import { EditorCanvas } from "./comps/EngineCanvases";
import { SceneContainer } from "./comps/SceneContainer";
export const PageEditor = () => {
useSignals();
@@ -68,20 +71,10 @@ export const PageEditor = () => {
}}
>
<div className="relative w-full overflow-hidden bg-transparent">
<div
id="video-scene-container"
className="relative"
style={{
width:
pageWidth.value -
(sidePanelVisible.value ? sidePanelWidth.value : 0) -
84,
height: pageHeight.value - timelineHeight.value - 68,
}}
>
<canvas id="video-scene" width="1280px" height="720px" />
<SceneContainer>
<EditorCanvas />
<PreviewFrameImage />
</div>
</SceneContainer>
{/* Top controls */}
<div
@@ -0,0 +1,44 @@
import { useCallback, useContext, useEffect } from "react";
import { EngineContext } from "../contexts/EngineContext";
export const EditorCanvas = () => {
const editorEngine = useContext(EngineContext);
const canvasCallbackRef = useCallback(
(node: HTMLCanvasElement) => {
if (node && editorEngine && editorEngine.setEditorCanvas) {
editorEngine.setEditorCanvas(node);
}
},
[editorEngine],
);
return (
<canvas
ref={canvasCallbackRef}
id="video-scene"
width="1280px"
height="720px"
/>
);
};
export const CameraViewCanvas = () => {
const editorEngine = useContext(EngineContext);
const canvasCallbackRef = useCallback(
(node: HTMLCanvasElement) => {
if (node && editorEngine) {
if (!editorEngine.canvasRenderCamReference) {
editorEngine.setCamViewCanvas(node);
} else {
editorEngine.updateCamViewCanvas(node);
}
}
//else just do nothing
},
[editorEngine],
);
return <canvas ref={canvasCallbackRef} id="camera-view" />;
};
@@ -9,6 +9,7 @@ import { editorState } from "~/pages/PageEnigma/signals/engine";
import { useSignals } from "@preact/signals-react/runtime";
import { sidePanelHeight } from "../../signals";
import { EditorStates } from "~/pages/PageEnigma/enums";
import { CameraViewCanvas } from "../EngineCanvases";
export const PreviewEngineCamera = () => {
useSignals();
@@ -60,7 +61,7 @@ export const PreviewEngineCamera = () => {
</Button>
</div>
<div className="box relative overflow-hidden rounded-b-lg border border-gray-600">
<canvas className="aspect-video" id="camera-view"></canvas>
<CameraViewCanvas />
<div className="absolute left-0 top-0 h-full w-full">
<LoadingDotsTyping isShowing={showLoader} />
</div>
@@ -40,9 +40,9 @@ export const PreviewFrameImage = () => {
} else {
return (
<img
alt="preview of the art style that renders over the 3d scene"
className="absolute inset-0 object-cover"
src={previewSrc.value}
id="video-scene"
style={{
width:
pageWidth.value -
@@ -0,0 +1,40 @@
import { useCallback, useContext } from "react";
import { useSignals } from "@preact/signals-react/runtime";
import { EngineContext } from "../contexts/EngineContext";
import { pageHeight, pageWidth } from "~/signals";
import {
timelineHeight,
sidePanelWidth,
sidePanelVisible,
} from "~/pages/PageEnigma/signals";
export const SceneContainer = ({ children }: { children: React.ReactNode }) => {
useSignals();
const editorEngine = useContext(EngineContext);
const callbackRef = useCallback(
(node: HTMLDivElement) => {
if (node && editorEngine && editorEngine.setSceneContainer) {
editorEngine.setSceneContainer(node);
}
},
[editorEngine],
);
return (
<div
ref={callbackRef}
id="video-scene-container"
className="relative"
style={{
width:
pageWidth.value -
(sidePanelVisible.value ? sidePanelWidth.value : 0) -
84,
height: pageHeight.value - timelineHeight.value - 68,
}}
>
{children}
</div>
);
};
@@ -1,4 +1,16 @@
import { createContext } from "react";
import Editor from "~/pages/PageEnigma/Editor/editor";
export const EngineContext = createContext<Editor | null>(null);
export interface EditorExpandedI extends Editor {
setSceneContainer: React.Dispatch<
React.SetStateAction<HTMLDivElement | null>
>;
setEditorCanvas: React.Dispatch<
React.SetStateAction<HTMLCanvasElement | null>
>;
setCamViewCanvas: React.Dispatch<
React.SetStateAction<HTMLCanvasElement | null>
>;
}
export const EngineContext = createContext<EditorExpandedI | null>(null);
@@ -1,5 +1,5 @@
import { ReactNode, useEffect, useState } from "react";
import { EngineContext } from "./EngineContext";
import { EngineContext, EditorExpandedI } from "./EngineContext";
import Editor from "~/pages/PageEnigma/Editor/editor";
@@ -9,25 +9,48 @@ interface Props {
}
export const EngineProvider = ({ sceneToken, children }: Props) => {
const [editor, setEditor] = useState<Editor | null>(null);
const [editor, setEditor] = useState<EditorExpandedI | null>(null);
const [sceneContainer, setSceneContainer] = useState<HTMLDivElement | null>(
null,
);
const [editorCanvas, setEditorCanvas] = useState<HTMLCanvasElement | null>(
null,
);
const [camViewCanvas, setCamViewCanvas] = useState<HTMLCanvasElement | null>(
null,
);
useEffect(() => {
setEditor((curr) => {
if (curr !== null) {
console.warn("Editor Engine already exist");
return curr;
}
return new Editor();
class EditorExpanded extends Editor implements EditorExpandedI {
setSceneContainer = setSceneContainer;
setEditorCanvas = setEditorCanvas;
setCamViewCanvas = setCamViewCanvas;
}
return new EditorExpanded();
});
}, []);
useEffect(() => {
if (editor && editor.can_initialize) {
if (
editor &&
editor.can_initialize &&
editorCanvas &&
camViewCanvas &&
sceneContainer
) {
editor.initialize({
sceneToken: sceneToken || "",
sceneContainerEl: sceneContainer,
editorCanvasEl: editorCanvas,
camViewCanvasEl: camViewCanvas,
});
}
}, [editor, sceneToken]);
}, [editor, sceneToken, editorCanvas, camViewCanvas, sceneContainer]);
return (
<EngineContext.Provider value={editor}>{children}</EngineContext.Provider>