The viewer JS/HTML/bundles are now committed in javascript/ (declared via pyproject [tool.comfy] web = "javascript") instead of being copied from the shared comfy-3d-viewers pip package at prestartup. Consequences: - requirements.txt is now just comfy-env (host env installs nothing else, per the never-touch-the-host-env principle) - prestartup_script.py no longer copies viewers or web_src; it keeps only setup_env() and the input-asset copies - WEB_DIRECTORY attribute removed; the pyproject declaration is the single source (served at /extensions/comfyui-geometrypack/, keyed by project name) - web_src/ deleted; its 6 widgets now live in javascript/js/ as the tracked source, edited directly - viewer_pbr.html hardcoded-fallback URL updated to the new mount name Iframe URLs auto-detect the mount from import.meta.url, so the folder rename and mount-name change are transparent at runtime.
113 lines
4.8 KiB
JavaScript
113 lines
4.8 KiB
JavaScript
/**
|
|
* VTK.js Point Cloud Preview Widget
|
|
* For point cloud visualization nodes (SAM3, DepthAnythingV3, etc.)
|
|
*/
|
|
|
|
import { app } from "../../../scripts/app.js";
|
|
import { EXTENSION_FOLDER, getViewerUrl } from "./utils/extensionFolder.js";
|
|
import { createContainer, createIframe, createInfoPanel, createWidgetOptions } from "./utils/uiComponents.js";
|
|
import { createViewerManager, createErrorHandler, buildViewUrl } from "./utils/postMessage.js";
|
|
|
|
const POINTCLOUD_NODES = ["SAM3D_PreviewPointCloud", "DA3_PreviewPointCloud"];
|
|
|
|
app.registerExtension({
|
|
name: "comfy3d.pointcloud.vtk",
|
|
|
|
async beforeRegisterNodeDef(nodeType, nodeData, app) {
|
|
if (POINTCLOUD_NODES.includes(nodeData.name)) {
|
|
|
|
const onNodeCreated = nodeType.prototype.onNodeCreated;
|
|
nodeType.prototype.onNodeCreated = function() {
|
|
const r = onNodeCreated ? onNodeCreated.apply(this, arguments) : undefined;
|
|
|
|
// Create container for viewer + info panel
|
|
const container = createContainer();
|
|
|
|
// Create iframe for VTK.js viewer
|
|
const iframe = createIframe(getViewerUrl("viewer_vtk"));
|
|
|
|
// Create info panel
|
|
const infoPanel = createInfoPanel("Point cloud will appear after execution");
|
|
|
|
// Add iframe and info panel to container
|
|
container.appendChild(iframe);
|
|
container.appendChild(infoPanel);
|
|
|
|
// Add widget
|
|
const widget = this.addDOMWidget("preview_vtk", "POINTCLOUD_PREVIEW_VTK", container, createWidgetOptions());
|
|
widget.computeSize = () => [512, 580];
|
|
|
|
// Store references
|
|
this._vtkIframe = iframe;
|
|
this._infoPanel = infoPanel;
|
|
|
|
// Create viewer manager
|
|
const viewerManager = createViewerManager(iframe, "[PointCloud VTK]");
|
|
|
|
// Listen for error messages
|
|
window.addEventListener('message', createErrorHandler(infoPanel, "[PointCloud VTK]"));
|
|
|
|
// Set initial node size
|
|
this.setSize([512, 580]);
|
|
|
|
return r;
|
|
};
|
|
|
|
// Handle execution
|
|
const onExecuted = nodeType.prototype.onExecuted;
|
|
nodeType.prototype.onExecuted = function(message) {
|
|
onExecuted?.apply(this, arguments);
|
|
|
|
// Get file path from ui.file_path
|
|
let filePath = null;
|
|
if (message?.ui?.file_path) {
|
|
filePath = Array.isArray(message.ui.file_path) ? message.ui.file_path[0] : message.ui.file_path;
|
|
} else if (message?.file_path) {
|
|
filePath = Array.isArray(message.file_path) ? message.file_path[0] : message.file_path;
|
|
}
|
|
|
|
if (filePath && filePath.trim() !== '') {
|
|
// Normalize path
|
|
filePath = filePath.replace(/\\/g, '/').trim();
|
|
|
|
// Build URL for ComfyUI view endpoint
|
|
const outputMatch = filePath.match(/(?:^|\/)(output|input)\/(.+)$/);
|
|
let url;
|
|
if (outputMatch) {
|
|
const [, type, relativePath] = outputMatch;
|
|
const pathParts = relativePath.split('/');
|
|
const filename = pathParts.pop();
|
|
const subfolder = pathParts.join('/');
|
|
url = `/view?filename=${encodeURIComponent(filename)}&type=${type}&subfolder=${encodeURIComponent(subfolder)}`;
|
|
} else {
|
|
const filename = filePath.split('/').pop();
|
|
url = `/view?filename=${encodeURIComponent(filename)}&type=output&subfolder=`;
|
|
}
|
|
|
|
// Update info panel
|
|
if (this._infoPanel) {
|
|
const filename = filePath.split('/').pop();
|
|
this._infoPanel.innerHTML = `<strong>File:</strong> ${filename}`;
|
|
}
|
|
|
|
// Send message to iframe
|
|
if (this._vtkIframe?.contentWindow) {
|
|
const sendMessage = () => {
|
|
this._vtkIframe.contentWindow.postMessage({
|
|
type: 'LOAD_MESH',
|
|
filepath: url
|
|
}, '*');
|
|
};
|
|
|
|
if (this._vtkIframe.contentDocument?.readyState === 'complete') {
|
|
sendMessage();
|
|
} else {
|
|
this._vtkIframe.addEventListener('load', () => setTimeout(sendMessage, 100), { once: true });
|
|
}
|
|
}
|
|
}
|
|
};
|
|
}
|
|
}
|
|
});
|