Files
PozzettiAndrea-ComfyUI-Geom…/javascript/js/viewer/core/ActorManager.js
T
PozzettiAndrea 414f759791 Vendor frontend JS into javascript/; drop the prestartup copy dance
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.
2026-08-12 17:37:09 +02:00

277 lines
8.0 KiB
JavaScript

/**
* ActorManager - Unified actor/property management for VTK viewers
*
* Handles actor configuration, appearance settings, edge visibility,
* and material properties. Works with both single actors and actor arrays.
*/
import { hexToRgb } from '../utils/ColorUtils.js';
/**
* Default actor appearance settings
*/
const DEFAULT_SETTINGS = {
color: [1.0, 1.0, 1.0], // White
edgeColor: [0.0, 0.0, 0.0], // Black edges
ambient: 0.4,
diffuse: 0.8,
specular: 0.3,
specularPower: 20,
edgeVisibility: false,
lineWidth: 1,
pointSize: 5,
representation: 2, // 0=Points, 1=Wireframe, 2=Surface
opacity: 1.0
};
/**
* Representation mode constants
*/
export const Representation = {
POINTS: 0,
WIREFRAME: 1,
SURFACE: 2
};
export class ActorManager {
/**
* Create an ActorManager
* @param {Object} renderWindow - VTK render window for triggering renders
*/
constructor(renderWindow) {
this.renderWindow = renderWindow;
this.actors = [];
this.settings = { ...DEFAULT_SETTINGS };
}
/**
* Add an actor to be managed
* @param {Object} actor - VTK actor instance
*/
addActor(actor) {
this.actors.push(actor);
this.configureActor(actor, this.settings);
}
/**
* Remove an actor from management
* @param {Object} actor - VTK actor instance
*/
removeActor(actor) {
const index = this.actors.indexOf(actor);
if (index > -1) {
this.actors.splice(index, 1);
}
}
/**
* Clear all managed actors
*/
clearActors() {
this.actors = [];
}
/**
* Set actors to manage (replaces existing)
* @param {Object[]} actors - Array of VTK actor instances
*/
setActors(actors) {
this.actors = Array.isArray(actors) ? actors : [actors];
this.actors.forEach(actor => this.configureActor(actor, this.settings));
}
/**
* Configure a single actor with settings
* @param {Object} actor - VTK actor instance
* @param {Object} settings - Settings object
*/
configureActor(actor, settings = {}) {
const merged = { ...this.settings, ...settings };
const property = actor.getProperty();
if (!property) {
console.warn('[ActorManager] Actor has no property object');
return;
}
// Color (supports hex string or RGB array)
if (merged.color) {
const rgb = typeof merged.color === 'string'
? hexToRgb(merged.color)
: merged.color;
property.setColor(...rgb);
}
// Edge color
if (merged.edgeColor) {
const rgb = typeof merged.edgeColor === 'string'
? hexToRgb(merged.edgeColor)
: merged.edgeColor;
property.setEdgeColor(...rgb);
}
// Lighting properties
if (merged.ambient !== undefined) property.setAmbient(merged.ambient);
if (merged.diffuse !== undefined) property.setDiffuse(merged.diffuse);
if (merged.specular !== undefined) property.setSpecular(merged.specular);
if (merged.specularPower !== undefined) property.setSpecularPower(merged.specularPower);
// Edge and line properties
if (merged.edgeVisibility !== undefined) property.setEdgeVisibility(merged.edgeVisibility);
if (merged.lineWidth !== undefined) property.setLineWidth(merged.lineWidth);
// Point properties
if (merged.pointSize !== undefined) property.setPointSize(merged.pointSize);
// Representation mode
if (merged.representation !== undefined) property.setRepresentation(merged.representation);
// Opacity
if (merged.opacity !== undefined) property.setOpacity(merged.opacity);
}
/**
* Apply settings to all managed actors
* @param {Object} settings - Settings object
* @param {boolean} render - Whether to trigger a render (default: true)
*/
applySettings(settings, render = true) {
this.settings = { ...this.settings, ...settings };
this.actors.forEach(actor => this.configureActor(actor, this.settings));
if (render) {
this.renderWindow.render();
}
}
/**
* Set edge visibility on all managed actors
* @param {boolean} visible - Whether edges should be visible
*/
setEdgeVisibility(visible) {
this.applySettings({ edgeVisibility: visible });
}
/**
* Set color on all managed actors
* @param {string|number[]} color - Hex string or RGB array (0-1 range)
*/
setColor(color) {
this.applySettings({ color });
}
/**
* Set opacity on all managed actors
* @param {number} opacity - Opacity value (0-1)
*/
setOpacity(opacity) {
this.applySettings({ opacity });
}
/**
* Set representation mode on all managed actors
* @param {number} mode - 0=Points, 1=Wireframe, 2=Surface
*/
setRepresentation(mode) {
this.applySettings({ representation: mode });
}
/**
* Set line width for edges/wireframe
* @param {number} width - Line width in pixels
*/
setLineWidth(width) {
this.applySettings({ lineWidth: width });
}
/**
* Set point size for point cloud rendering
* @param {number} size - Point size in pixels
*/
setPointSize(size) {
this.applySettings({ pointSize: size });
}
/**
* Get current settings
* @returns {Object} Current settings object
*/
getSettings() {
return { ...this.settings };
}
/**
* Apply settings from a settings panel (handles hex colors and number parsing)
* @param {Object} panelValues - Values from settings panel inputs
*/
applyPanelSettings(panelValues) {
const settings = {};
if (panelValues.meshColor !== undefined) {
settings.color = panelValues.meshColor;
}
if (panelValues.pointSize !== undefined) {
settings.pointSize = parseFloat(panelValues.pointSize);
}
if (panelValues.edgeWidth !== undefined) {
settings.lineWidth = parseFloat(panelValues.edgeWidth);
}
if (panelValues.representation !== undefined) {
settings.representation = parseInt(panelValues.representation, 10);
}
if (panelValues.opacity !== undefined) {
settings.opacity = parseFloat(panelValues.opacity);
}
this.applySettings(settings);
}
/**
* Configure for textured mode (disables scalar visibility, sets material)
* @param {Object} actor - VTK actor instance
*/
configureForTexture(actor) {
const property = actor.getProperty();
const mapper = actor.getMapper();
if (property) {
// Material settings for proper texture display
if (typeof property.setMetallic === 'function') {
property.setMetallic(0.0);
}
if (typeof property.setRoughness === 'function') {
property.setRoughness(1.0);
}
if (typeof property.setInterpolation === 'function') {
property.setInterpolation(2); // Phong shading
}
// Remove metallic/roughness texture if present (common GLTF issue)
if (typeof property.setRMTexture === 'function') {
property.setRMTexture(null);
}
if (typeof property.setMetallicRoughnessTexture === 'function') {
property.setMetallicRoughnessTexture(null);
}
}
if (mapper) {
mapper.setScalarVisibility(false);
mapper.modified();
}
}
/**
* Enable/disable scalar (field) visualization on mapper
* @param {Object} mapper - VTK mapper instance
* @param {boolean} enabled - Whether to show scalar colors
*/
setScalarVisibility(mapper, enabled) {
if (mapper) {
mapper.setScalarVisibility(enabled);
mapper.modified();
}
}
}
export default ActorManager;