sparing javascript lines
This commit is contained in:
+11
-11
@@ -52,7 +52,7 @@ class RemeshNode:
|
||||
"step": 0.01,
|
||||
"display": "number",
|
||||
"tooltip": "Target edge length for output triangles. Value is relative to mesh scale.",
|
||||
"backends": ["pymeshlab_isotropic", "cgal_isotropic"],
|
||||
"visible_when": {"backend": ["pymeshlab_isotropic", "cgal_isotropic"]},
|
||||
}),
|
||||
"iterations": ("INT", {
|
||||
"default": 3,
|
||||
@@ -60,7 +60,7 @@ class RemeshNode:
|
||||
"max": 20,
|
||||
"step": 1,
|
||||
"tooltip": "Number of remeshing passes. More iterations = smoother result, slower processing.",
|
||||
"backends": ["pymeshlab_isotropic", "cgal_isotropic"],
|
||||
"visible_when": {"backend": ["pymeshlab_isotropic", "cgal_isotropic"]},
|
||||
}),
|
||||
# PyMeshLab-specific
|
||||
"feature_angle": ("FLOAT", {
|
||||
@@ -69,18 +69,18 @@ class RemeshNode:
|
||||
"max": 180.0,
|
||||
"step": 1.0,
|
||||
"tooltip": "Angle threshold (degrees) for feature edge detection. Edges with dihedral angle greater than this are preserved as sharp creases.",
|
||||
"backends": ["pymeshlab_isotropic"],
|
||||
"visible_when": {"backend": ["pymeshlab_isotropic"]},
|
||||
}),
|
||||
"adaptive": (["true", "false"], {
|
||||
"default": "false",
|
||||
"tooltip": "Use curvature-adaptive edge lengths. Creates smaller triangles in high-curvature areas, larger triangles in flat areas.",
|
||||
"backends": ["pymeshlab_isotropic"],
|
||||
"visible_when": {"backend": ["pymeshlab_isotropic"]},
|
||||
}),
|
||||
# CGAL-specific
|
||||
"protect_boundaries": (["true", "false"], {
|
||||
"default": "true",
|
||||
"tooltip": "Lock boundary/open edges in place during remeshing. Prevents modification of mesh borders and holes.",
|
||||
"backends": ["cgal_isotropic"],
|
||||
"visible_when": {"backend": ["cgal_isotropic"]},
|
||||
}),
|
||||
# Blender voxel
|
||||
"voxel_size": ("FLOAT", {
|
||||
@@ -90,7 +90,7 @@ class RemeshNode:
|
||||
"step": 0.01,
|
||||
"display": "number",
|
||||
"tooltip": "Voxel size for Blender voxel remesh. Smaller = more detail, more faces. Output is always watertight.",
|
||||
"backends": ["blender_voxel"],
|
||||
"visible_when": {"backend": ["blender_voxel"]},
|
||||
}),
|
||||
# CuMesh / Quadriflow
|
||||
"target_face_count": ("INT", {
|
||||
@@ -99,7 +99,7 @@ class RemeshNode:
|
||||
"max": 5000000,
|
||||
"step": 1000,
|
||||
"tooltip": "Target number of output faces for cumesh/quadriflow backends.",
|
||||
"backends": ["cumesh", "blender_quadriflow"],
|
||||
"visible_when": {"backend": ["cumesh", "blender_quadriflow"]},
|
||||
}),
|
||||
# CuMesh specific (matches TRELLIS2)
|
||||
"remesh_band": ("FLOAT", {
|
||||
@@ -108,7 +108,7 @@ class RemeshNode:
|
||||
"max": 5.0,
|
||||
"step": 0.1,
|
||||
"tooltip": "Band width for CuMesh dual-contouring. Affects surface detail capture. Higher = smoother but may lose fine details.",
|
||||
"backends": ["cumesh"],
|
||||
"visible_when": {"backend": ["cumesh"]},
|
||||
}),
|
||||
# Instant Meshes specific
|
||||
"target_vertex_count": ("INT", {
|
||||
@@ -117,12 +117,12 @@ class RemeshNode:
|
||||
"max": 1000000,
|
||||
"step": 100,
|
||||
"tooltip": "Target vertex count for Instant Meshes output. Creates field-aligned quad-dominant mesh.",
|
||||
"backends": ["instant_meshes"],
|
||||
"visible_when": {"backend": ["instant_meshes"]},
|
||||
}),
|
||||
"deterministic": (["true", "false"], {
|
||||
"default": "true",
|
||||
"tooltip": "Use deterministic algorithm for reproducible results. Disable for potentially better quality but non-reproducible output.",
|
||||
"backends": ["instant_meshes"],
|
||||
"visible_when": {"backend": ["instant_meshes"]},
|
||||
}),
|
||||
"crease_angle": ("FLOAT", {
|
||||
"default": 0.0,
|
||||
@@ -130,7 +130,7 @@ class RemeshNode:
|
||||
"max": 180.0,
|
||||
"step": 1.0,
|
||||
"tooltip": "Angle threshold (degrees) for preserving sharp/crease edges in Instant Meshes. 0 = no crease preservation.",
|
||||
"backends": ["instant_meshes"],
|
||||
"visible_when": {"backend": ["instant_meshes"]},
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,7 +51,7 @@ class FillHolesNode:
|
||||
"min": 0.001,
|
||||
"max": 1.0,
|
||||
"step": 0.001,
|
||||
"backends": ["cumesh"],
|
||||
"visible_when": {"method": ["cumesh"]},
|
||||
}),
|
||||
# PyMeshLab parameter
|
||||
"maxholesize": ("INT", {
|
||||
@@ -59,7 +59,7 @@ class FillHolesNode:
|
||||
"min": 1,
|
||||
"max": 100000,
|
||||
"step": 100,
|
||||
"backends": ["pymeshlab"],
|
||||
"visible_when": {"method": ["pymeshlab"]},
|
||||
}),
|
||||
},
|
||||
}
|
||||
|
||||
@@ -58,10 +58,14 @@ class TextureToGeometryNode:
|
||||
"min": 4,
|
||||
"max": 12,
|
||||
"step": 1,
|
||||
"tooltip": "Octree depth for Poisson reconstruction (higher = more detail)"
|
||||
"tooltip": "Octree depth for Poisson reconstruction (higher = more detail)",
|
||||
"visible_when": {"backend": ["poisson_pymeshlab", "poisson_open3d"]},
|
||||
}),
|
||||
"invert_height": (["false", "true"], {"default": "false"}),
|
||||
"smooth_normals": (["true", "false"], {"default": "true"}),
|
||||
"smooth_normals": (["true", "false"], {
|
||||
"default": "true",
|
||||
"visible_when": {"backend": ["grid"]},
|
||||
}),
|
||||
"skip_black": (["false", "true"], {"default": "false", "tooltip": "Skip faces connected to near-black pixels in the depth map"}),
|
||||
"black_threshold": ("FLOAT", {
|
||||
"default": 0.01,
|
||||
|
||||
@@ -53,74 +53,97 @@ class TransformMeshNode:
|
||||
"default": 0.0,
|
||||
"min": -1000.0,
|
||||
"max": 1000.0,
|
||||
"step": 0.1
|
||||
"step": 0.1,
|
||||
"visible_when": {"operation": ["translate"]},
|
||||
}),
|
||||
"translate_y": ("FLOAT", {
|
||||
"default": 0.0,
|
||||
"min": -1000.0,
|
||||
"max": 1000.0,
|
||||
"step": 0.1
|
||||
"step": 0.1,
|
||||
"visible_when": {"operation": ["translate"]},
|
||||
}),
|
||||
"translate_z": ("FLOAT", {
|
||||
"default": 0.0,
|
||||
"min": -1000.0,
|
||||
"max": 1000.0,
|
||||
"step": 0.1
|
||||
"step": 0.1,
|
||||
"visible_when": {"operation": ["translate"]},
|
||||
}),
|
||||
# Rotate (degrees)
|
||||
"rotate_x": ("FLOAT", {
|
||||
"default": 0.0,
|
||||
"min": -360.0,
|
||||
"max": 360.0,
|
||||
"step": 1.0
|
||||
"step": 1.0,
|
||||
"visible_when": {"operation": ["rotate"]},
|
||||
}),
|
||||
"rotate_y": ("FLOAT", {
|
||||
"default": 0.0,
|
||||
"min": -360.0,
|
||||
"max": 360.0,
|
||||
"step": 1.0
|
||||
"step": 1.0,
|
||||
"visible_when": {"operation": ["rotate"]},
|
||||
}),
|
||||
"rotate_z": ("FLOAT", {
|
||||
"default": 0.0,
|
||||
"min": -360.0,
|
||||
"max": 360.0,
|
||||
"step": 1.0
|
||||
"step": 1.0,
|
||||
"visible_when": {"operation": ["rotate"]},
|
||||
}),
|
||||
# Scale
|
||||
"scale_uniform": ("FLOAT", {
|
||||
"default": 1.0,
|
||||
"min": 0.001,
|
||||
"max": 1000.0,
|
||||
"step": 0.1
|
||||
"step": 0.1,
|
||||
"visible_when": {"operation": ["scale"]},
|
||||
}),
|
||||
"scale_x": ("FLOAT", {
|
||||
"default": 1.0,
|
||||
"min": 0.001,
|
||||
"max": 1000.0,
|
||||
"step": 0.1
|
||||
"step": 0.1,
|
||||
"visible_when": {"operation": ["scale"]},
|
||||
}),
|
||||
"scale_y": ("FLOAT", {
|
||||
"default": 1.0,
|
||||
"min": 0.001,
|
||||
"max": 1000.0,
|
||||
"step": 0.1
|
||||
"step": 0.1,
|
||||
"visible_when": {"operation": ["scale"]},
|
||||
}),
|
||||
"scale_z": ("FLOAT", {
|
||||
"default": 1.0,
|
||||
"min": 0.001,
|
||||
"max": 1000.0,
|
||||
"step": 0.1
|
||||
"step": 0.1,
|
||||
"visible_when": {"operation": ["scale"]},
|
||||
}),
|
||||
# Mirror
|
||||
"mirror_axis": (["x", "y", "z"], {"default": "x"}),
|
||||
"mirror_axis": (["x", "y", "z"], {
|
||||
"default": "x",
|
||||
"visible_when": {"operation": ["mirror"]},
|
||||
}),
|
||||
# Center options
|
||||
"center_x": (["true", "false"], {"default": "true"}),
|
||||
"center_y": (["true", "false"], {"default": "true"}),
|
||||
"center_z": (["true", "false"], {"default": "true"}),
|
||||
"center_x": (["true", "false"], {
|
||||
"default": "true",
|
||||
"visible_when": {"operation": ["center"]},
|
||||
}),
|
||||
"center_y": (["true", "false"], {
|
||||
"default": "true",
|
||||
"visible_when": {"operation": ["center"]},
|
||||
}),
|
||||
"center_z": (["true", "false"], {
|
||||
"default": "true",
|
||||
"visible_when": {"operation": ["center"]},
|
||||
}),
|
||||
# Matrix (16 comma-separated floats, row-major)
|
||||
"matrix_string": ("STRING", {
|
||||
"default": "1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1",
|
||||
"multiline": False
|
||||
"multiline": False,
|
||||
"visible_when": {"operation": ["apply_matrix"]},
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
+22
-10
@@ -52,25 +52,29 @@ class UVUnwrapNode:
|
||||
"default": 90.0,
|
||||
"min": 0.0,
|
||||
"max": 359.9,
|
||||
"step": 1.0
|
||||
"step": 1.0,
|
||||
"visible_when": {"method": ["cumesh"]},
|
||||
}),
|
||||
"chart_refine_iterations": ("INT", {
|
||||
"default": 0,
|
||||
"min": 0,
|
||||
"max": 10,
|
||||
"step": 1
|
||||
"step": 1,
|
||||
"visible_when": {"method": ["cumesh"]},
|
||||
}),
|
||||
"chart_global_iterations": ("INT", {
|
||||
"default": 1,
|
||||
"min": 0,
|
||||
"max": 10,
|
||||
"step": 1
|
||||
"step": 1,
|
||||
"visible_when": {"method": ["cumesh"]},
|
||||
}),
|
||||
"chart_smooth_strength": ("INT", {
|
||||
"default": 1,
|
||||
"min": 0,
|
||||
"max": 10,
|
||||
"step": 1
|
||||
"step": 1,
|
||||
"visible_when": {"method": ["cumesh"]},
|
||||
}),
|
||||
|
||||
# libigl_arap parameters
|
||||
@@ -78,7 +82,8 @@ class UVUnwrapNode:
|
||||
"default": 10,
|
||||
"min": 1,
|
||||
"max": 100,
|
||||
"step": 1
|
||||
"step": 1,
|
||||
"visible_when": {"method": ["libigl_arap"]},
|
||||
}),
|
||||
|
||||
# Blender smart_uv parameters
|
||||
@@ -86,28 +91,35 @@ class UVUnwrapNode:
|
||||
"default": 66.0,
|
||||
"min": 1.0,
|
||||
"max": 89.0,
|
||||
"step": 1.0
|
||||
"step": 1.0,
|
||||
"visible_when": {"method": ["blender_smart"]},
|
||||
}),
|
||||
"island_margin": ("FLOAT", {
|
||||
"default": 0.02,
|
||||
"min": 0.0,
|
||||
"max": 1.0,
|
||||
"step": 0.01
|
||||
"step": 0.01,
|
||||
"visible_when": {"method": ["blender_smart"]},
|
||||
}),
|
||||
|
||||
# Blender projection parameters
|
||||
"scale_to_bounds": (["true", "false"], {"default": "true"}),
|
||||
"scale_to_bounds": (["true", "false"], {
|
||||
"default": "true",
|
||||
"visible_when": {"method": ["blender_smart", "blender_cube", "blender_cylinder", "blender_sphere"]},
|
||||
}),
|
||||
"cube_size": ("FLOAT", {
|
||||
"default": 1.0,
|
||||
"min": 0.1,
|
||||
"max": 10.0,
|
||||
"step": 0.1
|
||||
"step": 0.1,
|
||||
"visible_when": {"method": ["blender_cube"]},
|
||||
}),
|
||||
"cylinder_radius": ("FLOAT", {
|
||||
"default": 1.0,
|
||||
"min": 0.1,
|
||||
"max": 10.0,
|
||||
"step": 0.1
|
||||
"step": 0.1,
|
||||
"visible_when": {"method": ["blender_cylinder"]},
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
+46
-75
@@ -3,126 +3,97 @@
|
||||
|
||||
"""
|
||||
GeometryPack PreStartup Script
|
||||
- Generates backend_mappings.json for dynamic widget visibility
|
||||
- Generates dynamic widget visibility mappings
|
||||
- Copies example 3D assets to ComfyUI input folder on startup
|
||||
"""
|
||||
import os
|
||||
import re
|
||||
import json
|
||||
import os
|
||||
import shutil
|
||||
|
||||
|
||||
def generate_backend_mappings():
|
||||
"""
|
||||
Parse Python node files and generate backend_mappings.json for JS.
|
||||
|
||||
Reads 'backends' metadata from INPUT_TYPES in node files and writes
|
||||
a JSON file that JavaScript can fetch to show/hide widgets dynamically.
|
||||
"""
|
||||
custom_node_dir = os.path.dirname(os.path.abspath(__file__))
|
||||
|
||||
# Node files to parse: (file_path, node_class, backend_widget_name)
|
||||
node_files = [
|
||||
("nodes/remeshing/remesh.py", "GeomPackRemesh", "backend"),
|
||||
("nodes/repair/fill_holes.py", "GeomPackFillHoles", "method"),
|
||||
]
|
||||
|
||||
mappings = {}
|
||||
backend_widgets = {}
|
||||
|
||||
for rel_path, node_class, backend_widget in node_files:
|
||||
file_path = os.path.join(custom_node_dir, rel_path)
|
||||
if not os.path.exists(file_path):
|
||||
print(f"[GeometryPack] Warning: {rel_path} not found, skipping")
|
||||
continue
|
||||
|
||||
with open(file_path, 'r') as f:
|
||||
source = f.read()
|
||||
|
||||
# Extract param -> backends mapping using regex
|
||||
# Matches: "param_name": (TYPE, {..., "backends": ["a", "b"], ...})
|
||||
node_mapping = {}
|
||||
pattern = r'"(\w+)":\s*\([^)]+\{[^}]*"backends":\s*\[([^\]]+)\]'
|
||||
|
||||
for match in re.finditer(pattern, source, re.DOTALL):
|
||||
param_name = match.group(1)
|
||||
backends_str = match.group(2)
|
||||
backends = [b.strip().strip('"\'') for b in backends_str.split(',')]
|
||||
|
||||
for backend in backends:
|
||||
if backend not in node_mapping:
|
||||
node_mapping[backend] = []
|
||||
node_mapping[backend].append(param_name)
|
||||
|
||||
if node_mapping:
|
||||
mappings[node_class] = node_mapping
|
||||
backend_widgets[node_class] = backend_widget
|
||||
print(f"[GeometryPack] Parsed {node_class}: {len(node_mapping)} backends")
|
||||
|
||||
# Write to web/js/backend_mappings.json
|
||||
output = {
|
||||
"mappings": mappings,
|
||||
"backend_widgets": backend_widgets,
|
||||
}
|
||||
|
||||
output_path = os.path.join(custom_node_dir, "web", "js", "backend_mappings.json")
|
||||
os.makedirs(os.path.dirname(output_path), exist_ok=True)
|
||||
|
||||
with open(output_path, 'w') as f:
|
||||
json.dump(output, f, indent=2)
|
||||
|
||||
print(f"[GeometryPack] Generated backend_mappings.json")
|
||||
|
||||
def copy_example_assets():
|
||||
"""Copy all files and folders from assets/ directory to ComfyUI input/3d directory."""
|
||||
try:
|
||||
import folder_paths
|
||||
|
||||
|
||||
input_folder = folder_paths.get_input_directory()
|
||||
custom_node_dir = os.path.dirname(os.path.abspath(__file__))
|
||||
|
||||
|
||||
# Create input/3d subdirectory
|
||||
input_3d_folder = os.path.join(input_folder, "3d")
|
||||
os.makedirs(input_3d_folder, exist_ok=True)
|
||||
|
||||
|
||||
# Copy entire assets/ folder structure
|
||||
assets_folder = os.path.join(custom_node_dir, "assets")
|
||||
if not os.path.exists(assets_folder):
|
||||
print(f"[GeometryPack] Warning: assets folder not found at {assets_folder}")
|
||||
return
|
||||
|
||||
|
||||
copied_count = 0
|
||||
for root, dirs, files in os.walk(assets_folder):
|
||||
# Calculate relative path from assets folder
|
||||
rel_path = os.path.relpath(root, assets_folder)
|
||||
|
||||
|
||||
# Create corresponding subdirectory in destination
|
||||
if rel_path != '.':
|
||||
dest_dir = os.path.join(input_3d_folder, rel_path)
|
||||
os.makedirs(dest_dir, exist_ok=True)
|
||||
else:
|
||||
dest_dir = input_3d_folder
|
||||
|
||||
|
||||
# Copy files
|
||||
for file in files:
|
||||
source_file = os.path.join(root, file)
|
||||
dest_file = os.path.join(dest_dir, file)
|
||||
|
||||
|
||||
if not os.path.exists(dest_file):
|
||||
shutil.copy2(source_file, dest_file)
|
||||
copied_count += 1
|
||||
# Show relative path for clarity
|
||||
rel_dest = os.path.join(rel_path, file) if rel_path != '.' else file
|
||||
print(f"[GeometryPack] Copied {rel_dest} to input/3d/")
|
||||
|
||||
|
||||
if copied_count > 0:
|
||||
print(f"[GeometryPack] [OK] Copied {copied_count} asset(s) to {input_3d_folder}")
|
||||
else:
|
||||
print(f"[GeometryPack] All assets already exist in {input_3d_folder}")
|
||||
|
||||
|
||||
except Exception as e:
|
||||
print(f"[GeometryPack] Error copying assets: {e}")
|
||||
|
||||
|
||||
def generate_widget_mappings():
|
||||
"""Generate dynamic widget visibility mappings using comfy-dynamic-widgets."""
|
||||
try:
|
||||
from comfy_dynamic_widgets import scan_all_nodes, generate_mappings
|
||||
|
||||
# Scan all nodes for visible_when metadata
|
||||
configs = scan_all_nodes()
|
||||
|
||||
if not configs:
|
||||
print("[GeometryPack] No visible_when metadata found in any nodes")
|
||||
return
|
||||
|
||||
# Generate JS-friendly mappings
|
||||
mappings = generate_mappings(configs)
|
||||
|
||||
# Write to web/js/mappings.json
|
||||
custom_node_dir = os.path.dirname(os.path.abspath(__file__))
|
||||
output_path = os.path.join(custom_node_dir, "web", "js", "mappings.json")
|
||||
|
||||
with open(output_path, "w") as f:
|
||||
json.dump(mappings, f, indent=2)
|
||||
|
||||
node_count = len(configs)
|
||||
selector_count = sum(len(c.get("selectors", {})) for c in configs.values())
|
||||
print(f"[GeometryPack] Generated widget mappings for {node_count} nodes ({selector_count} selectors)")
|
||||
|
||||
except ImportError:
|
||||
print("[GeometryPack] Warning: comfy-dynamic-widgets not installed, skipping widget mappings")
|
||||
except Exception as e:
|
||||
print(f"[GeometryPack] Error generating widget mappings: {e}")
|
||||
|
||||
|
||||
# Run on import
|
||||
generate_backend_mappings()
|
||||
copy_example_assets()
|
||||
generate_widget_mappings()
|
||||
copy_example_assets()
|
||||
|
||||
@@ -10,6 +10,7 @@ classifiers = [
|
||||
]
|
||||
dependencies = [
|
||||
"comfy-env==0.0.19",
|
||||
"comfy-dynamic-widgets>=0.0.2",
|
||||
"trimesh>=3.15.0",
|
||||
"libigl>=2.6.1",
|
||||
"numpy>=1.21.0",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
# Installation dependencies (for install.py optimization)
|
||||
comfy-env==0.0.19
|
||||
comfy-dynamic-widgets>=0.0.2
|
||||
requests>=2.25.0
|
||||
tqdm>=4.60.0
|
||||
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
{
|
||||
"mappings": {
|
||||
"GeomPackRemesh": {
|
||||
"pymeshlab_isotropic": [
|
||||
"target_edge_length",
|
||||
"iterations",
|
||||
"feature_angle",
|
||||
"adaptive"
|
||||
],
|
||||
"cgal_isotropic": [
|
||||
"target_edge_length",
|
||||
"iterations",
|
||||
"protect_boundaries"
|
||||
],
|
||||
"blender_voxel": [
|
||||
"voxel_size"
|
||||
],
|
||||
"cumesh": [
|
||||
"target_face_count",
|
||||
"remesh_band"
|
||||
],
|
||||
"blender_quadriflow": [
|
||||
"target_face_count"
|
||||
],
|
||||
"instant_meshes": [
|
||||
"target_vertex_count",
|
||||
"deterministic",
|
||||
"crease_angle"
|
||||
]
|
||||
},
|
||||
"GeomPackFillHoles": {
|
||||
"cumesh": [
|
||||
"perimeter"
|
||||
],
|
||||
"pymeshlab": [
|
||||
"maxholesize"
|
||||
]
|
||||
}
|
||||
},
|
||||
"backend_widgets": {
|
||||
"GeomPackRemesh": "backend",
|
||||
"GeomPackFillHoles": "method"
|
||||
}
|
||||
}
|
||||
+85
-60
@@ -1,24 +1,29 @@
|
||||
/**
|
||||
* Dynamic Widget Visibility for Multi-Backend Nodes
|
||||
* Dynamic Widget Visibility for ComfyUI
|
||||
*
|
||||
* Shows/hides widgets based on selected backend.
|
||||
* Mappings are loaded from backend_mappings.json (generated by prestartup_script.py).
|
||||
* Shows/hides widgets based on selector widget values.
|
||||
* Mappings are loaded from mappings.json (generated by prestartup_script.py).
|
||||
*
|
||||
* Usage in node definitions:
|
||||
* "voxel_size": ("FLOAT", {
|
||||
* "default": 0.1,
|
||||
* "visible_when": {"backend": ["blender_voxel"]},
|
||||
* })
|
||||
*/
|
||||
import { app } from "../../../scripts/app.js";
|
||||
|
||||
console.log("[GeomPack DynamicWidgets] Loading extension...");
|
||||
console.log("[DynamicWidgets] Loading extension...");
|
||||
|
||||
// Will be populated from JSON
|
||||
let BACKEND_MAPPINGS = {};
|
||||
let BACKEND_WIDGET_NAME = {};
|
||||
let MAPPINGS = null;
|
||||
|
||||
/**
|
||||
* Fetch mappings from generated JSON (try both casing for manager installs)
|
||||
* Fetch mappings from generated JSON
|
||||
*/
|
||||
async function loadMappings() {
|
||||
const paths = [
|
||||
"/extensions/ComfyUI-GeometryPack/js/backend_mappings.json",
|
||||
"/extensions/comfyui-geometrypack/js/backend_mappings.json",
|
||||
"/extensions/ComfyUI-GeometryPack/js/mappings.json",
|
||||
"/extensions/comfyui-geometrypack/js/mappings.json",
|
||||
];
|
||||
|
||||
for (const path of paths) {
|
||||
@@ -26,16 +31,16 @@ async function loadMappings() {
|
||||
const response = await fetch(path);
|
||||
if (response.ok) {
|
||||
const data = await response.json();
|
||||
BACKEND_MAPPINGS = data.mappings || {};
|
||||
BACKEND_WIDGET_NAME = data.backend_widgets || {};
|
||||
console.log("[GeomPack DynamicWidgets] Loaded mappings from", path);
|
||||
MAPPINGS = data;
|
||||
const nodeCount = Object.keys(data.nodes || {}).length;
|
||||
console.log(`[DynamicWidgets] Loaded mappings for ${nodeCount} nodes from ${path}`);
|
||||
return true;
|
||||
}
|
||||
} catch (e) {
|
||||
// Try next path
|
||||
}
|
||||
}
|
||||
console.warn("[GeomPack DynamicWidgets] Could not load backend_mappings.json");
|
||||
console.warn("[DynamicWidgets] Could not load mappings.json - no dynamic widgets configured");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -43,37 +48,49 @@ async function loadMappings() {
|
||||
loadMappings();
|
||||
|
||||
/**
|
||||
* Update widget visibility based on selected backend
|
||||
* Get the node configuration from mappings
|
||||
*/
|
||||
function updateWidgetVisibility(node, selectedBackend) {
|
||||
const nodeClass = node.comfyClass;
|
||||
const mapping = BACKEND_MAPPINGS[nodeClass];
|
||||
if (!mapping) return;
|
||||
function getNodeConfig(nodeClass) {
|
||||
if (!MAPPINGS || !MAPPINGS.nodes) return null;
|
||||
return MAPPINGS.nodes[nodeClass] || null;
|
||||
}
|
||||
|
||||
const visibleWidgets = mapping[selectedBackend] || [];
|
||||
const backendWidgetName = BACKEND_WIDGET_NAME[nodeClass];
|
||||
/**
|
||||
* Update widget visibility based on selector value
|
||||
*/
|
||||
function updateWidgetVisibility(node, selectorName, selectedValue) {
|
||||
const nodeConfig = getNodeConfig(node.comfyClass);
|
||||
if (!nodeConfig || !nodeConfig.selectors) return;
|
||||
|
||||
console.log(`[GeomPack DynamicWidgets] ${nodeClass}: backend="${selectedBackend}", visible=[${visibleWidgets.join(', ')}]`);
|
||||
const selectorConfig = nodeConfig.selectors[selectorName];
|
||||
if (!selectorConfig) return;
|
||||
|
||||
// Get widgets that should be visible for this selector value
|
||||
const visibleWidgets = selectorConfig[selectedValue] || [];
|
||||
|
||||
// Get all widgets controlled by this selector
|
||||
const allControlledWidgets = new Set();
|
||||
for (const widgets of Object.values(selectorConfig)) {
|
||||
for (const w of widgets) {
|
||||
allControlledWidgets.add(w);
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`[DynamicWidgets] ${node.comfyClass}: ${selectorName}="${selectedValue}", visible=[${visibleWidgets.join(', ')}]`);
|
||||
|
||||
let visibilityChanged = false;
|
||||
|
||||
for (const widget of node.widgets || []) {
|
||||
// Skip the backend/method selector itself and the mesh input
|
||||
if (widget.name === backendWidgetName || widget.name === "trimesh" || widget.name === "mesh") continue;
|
||||
// Only control widgets that are in our mapping
|
||||
if (!allControlledWidgets.has(widget.name)) continue;
|
||||
|
||||
// Check if this widget should be visible for the current backend
|
||||
const allWidgets = Object.values(mapping).flat();
|
||||
const isBackendWidget = allWidgets.includes(widget.name);
|
||||
const shouldShow = visibleWidgets.includes(widget.name);
|
||||
const wasHidden = widget.hidden;
|
||||
widget.hidden = !shouldShow;
|
||||
|
||||
if (isBackendWidget) {
|
||||
const shouldShow = visibleWidgets.includes(widget.name);
|
||||
const wasHidden = widget.hidden;
|
||||
widget.hidden = !shouldShow;
|
||||
|
||||
if (wasHidden !== widget.hidden) {
|
||||
visibilityChanged = true;
|
||||
console.log(`[GeomPack DynamicWidgets] Widget "${widget.name}": hidden=${widget.hidden}`);
|
||||
}
|
||||
if (wasHidden !== widget.hidden) {
|
||||
visibilityChanged = true;
|
||||
console.log(`[DynamicWidgets] Widget "${widget.name}": hidden=${widget.hidden}`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,37 +100,45 @@ function updateWidgetVisibility(node, selectedBackend) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up visibility control for a selector widget
|
||||
*/
|
||||
function setupSelectorWidget(node, selectorName) {
|
||||
const selectorWidget = node.widgets?.find(w => w.name === selectorName);
|
||||
if (!selectorWidget) {
|
||||
console.log(`[DynamicWidgets] Selector widget "${selectorName}" not found on ${node.comfyClass}`);
|
||||
return;
|
||||
}
|
||||
|
||||
// Store original callback
|
||||
const originalCallback = selectorWidget.callback;
|
||||
|
||||
// Override callback to update visibility on change
|
||||
selectorWidget.callback = function(value) {
|
||||
if (originalCallback) originalCallback.call(this, value);
|
||||
updateWidgetVisibility(node, selectorName, value);
|
||||
};
|
||||
|
||||
// Initial visibility update (with delay to ensure widgets are created)
|
||||
setTimeout(() => {
|
||||
updateWidgetVisibility(node, selectorName, selectorWidget.value);
|
||||
}, 50);
|
||||
}
|
||||
|
||||
app.registerExtension({
|
||||
name: "GeomPack.DynamicWidgets",
|
||||
name: "Comfy.DynamicWidgets",
|
||||
|
||||
nodeCreated(node) {
|
||||
const nodeClass = node.comfyClass;
|
||||
if (!BACKEND_MAPPINGS[nodeClass]) return;
|
||||
const nodeConfig = getNodeConfig(node.comfyClass);
|
||||
if (!nodeConfig || !nodeConfig.selectors) return;
|
||||
|
||||
console.log(`[GeomPack DynamicWidgets] Setting up node: ${nodeClass}`);
|
||||
console.log(`[DynamicWidgets] Setting up node: ${node.comfyClass}`);
|
||||
|
||||
const backendWidgetName = BACKEND_WIDGET_NAME[nodeClass];
|
||||
const backendWidget = node.widgets?.find(w => w.name === backendWidgetName);
|
||||
|
||||
if (!backendWidget) {
|
||||
console.log(`[GeomPack DynamicWidgets] No ${backendWidgetName} widget found`);
|
||||
return;
|
||||
// Set up each selector
|
||||
for (const selectorName of Object.keys(nodeConfig.selectors)) {
|
||||
setupSelectorWidget(node, selectorName);
|
||||
}
|
||||
|
||||
// Store original callback
|
||||
const originalCallback = backendWidget.callback;
|
||||
|
||||
// Override callback to update visibility on change
|
||||
backendWidget.callback = function(value) {
|
||||
if (originalCallback) originalCallback.call(this, value);
|
||||
updateWidgetVisibility(node, value);
|
||||
};
|
||||
|
||||
// Initial visibility update (with delay to ensure widgets are created)
|
||||
setTimeout(() => {
|
||||
updateWidgetVisibility(node, backendWidget.value);
|
||||
}, 50);
|
||||
}
|
||||
});
|
||||
|
||||
console.log("[GeomPack DynamicWidgets] Extension registered");
|
||||
console.log("[DynamicWidgets] Extension registered");
|
||||
|
||||
@@ -1,183 +0,0 @@
|
||||
/**
|
||||
* ComfyUI-GeometryPack Dynamic Parameter Management
|
||||
*
|
||||
* This extension manages the visibility of backend-specific parameters
|
||||
* in the Depth Map to Mesh node based on the selected backend.
|
||||
*/
|
||||
|
||||
import { app } from "../../../scripts/app.js";
|
||||
|
||||
const DEBUG = false;
|
||||
|
||||
function log(...args) {
|
||||
if (DEBUG) {
|
||||
console.log("[GeomPack-TextureToGeometry]", ...args);
|
||||
}
|
||||
}
|
||||
|
||||
// Helper function to hide a widget
|
||||
function hideWidget(node, widget) {
|
||||
if (!widget) return;
|
||||
if (widget._hidden) {
|
||||
log("Widget already hidden:", widget.name);
|
||||
return;
|
||||
}
|
||||
|
||||
log("Hiding widget:", widget.name);
|
||||
|
||||
const index = node.widgets.indexOf(widget);
|
||||
if (index === -1) {
|
||||
log(" ERROR: Widget not found in array!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!widget.origType) {
|
||||
widget.origType = widget.type;
|
||||
widget.origComputeSize = widget.computeSize;
|
||||
widget.origSerializeValue = widget.serializeValue;
|
||||
}
|
||||
|
||||
widget._originalIndex = index;
|
||||
widget._hidden = true;
|
||||
|
||||
node.widgets.splice(index, 1);
|
||||
|
||||
log(" Widget removed from index:", index);
|
||||
|
||||
if (widget.linkedWidgets) {
|
||||
widget.linkedWidgets.forEach(w => hideWidget(node, w));
|
||||
}
|
||||
}
|
||||
|
||||
// Helper function to show a widget
|
||||
function showWidget(node, widget) {
|
||||
if (!widget) return;
|
||||
if (!widget._hidden) {
|
||||
log("Widget already visible:", widget.name);
|
||||
return;
|
||||
}
|
||||
|
||||
log("Showing widget:", widget.name);
|
||||
|
||||
if (widget.origType) {
|
||||
widget.type = widget.origType;
|
||||
widget.computeSize = widget.origComputeSize;
|
||||
if (widget.origSerializeValue) {
|
||||
widget.serializeValue = widget.origSerializeValue;
|
||||
}
|
||||
}
|
||||
|
||||
const insertIndex = Math.min(widget._originalIndex, node.widgets.length);
|
||||
node.widgets.splice(insertIndex, 0, widget);
|
||||
|
||||
log(" Widget restored to index:", insertIndex);
|
||||
|
||||
widget._hidden = false;
|
||||
|
||||
if (widget.linkedWidgets) {
|
||||
widget.linkedWidgets.forEach(w => showWidget(node, w));
|
||||
}
|
||||
}
|
||||
|
||||
// Helper to update node size and redraw
|
||||
function updateNodeSize(node) {
|
||||
node.setDirtyCanvas(true, true);
|
||||
if (app.graph) {
|
||||
app.graph.setDirtyCanvas(true, true);
|
||||
}
|
||||
|
||||
requestAnimationFrame(() => {
|
||||
const newSize = node.computeSize();
|
||||
node.setSize([node.size[0], newSize[1]]);
|
||||
node.setDirtyCanvas(true, true);
|
||||
|
||||
if (app.canvas) {
|
||||
app.canvas.setDirty(true, true);
|
||||
}
|
||||
|
||||
requestAnimationFrame(() => {
|
||||
if (app.canvas) {
|
||||
app.canvas.draw(true, true);
|
||||
}
|
||||
log("Widget visibility update complete");
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Main extension registration
|
||||
app.registerExtension({
|
||||
name: "geompack.texture_to_geometry_dynamic",
|
||||
|
||||
async nodeCreated(node) {
|
||||
if (node.comfyClass === "GeomPackTextureToGeometry") {
|
||||
setTimeout(() => {
|
||||
this.setupTextureToGeometryNode(node);
|
||||
}, 100);
|
||||
}
|
||||
},
|
||||
|
||||
setupTextureToGeometryNode(node) {
|
||||
log("Setting up GeomPackTextureToGeometry node");
|
||||
|
||||
// Find the backend selector widget
|
||||
const backendWidget = node.widgets?.find(w => w.name === "backend");
|
||||
if (!backendWidget) {
|
||||
log("ERROR: Backend widget not found!");
|
||||
return;
|
||||
}
|
||||
|
||||
log("Backend widget found, current value:", backendWidget.value);
|
||||
|
||||
// Find all parameter widgets
|
||||
const poissonDepthWidget = node.widgets?.find(w => w.name === "poisson_depth");
|
||||
const smoothNormalsWidget = node.widgets?.find(w => w.name === "smooth_normals");
|
||||
|
||||
log("Found parameter widgets:", {
|
||||
poisson_depth: !!poissonDepthWidget,
|
||||
smooth_normals: !!smoothNormalsWidget
|
||||
});
|
||||
|
||||
// Function to update widget visibility based on selected backend
|
||||
const updateWidgetVisibility = (selectedBackend) => {
|
||||
log("Updating widget visibility for backend:", selectedBackend);
|
||||
|
||||
// Hide all backend-specific parameters first
|
||||
hideWidget(node, poissonDepthWidget);
|
||||
hideWidget(node, smoothNormalsWidget);
|
||||
|
||||
// Show relevant parameters based on backend
|
||||
if (selectedBackend === "grid") {
|
||||
// Grid: show smooth_normals, hide poisson_depth
|
||||
showWidget(node, smoothNormalsWidget);
|
||||
} else if (selectedBackend === "poisson_pymeshlab" || selectedBackend === "poisson_open3d") {
|
||||
// Poisson backends: show poisson_depth, hide smooth_normals
|
||||
showWidget(node, poissonDepthWidget);
|
||||
} else if (selectedBackend === "delaunay_2d") {
|
||||
// Delaunay: hide both poisson_depth and smooth_normals
|
||||
// (already hidden above)
|
||||
}
|
||||
|
||||
updateNodeSize(node);
|
||||
};
|
||||
|
||||
// Store original callback
|
||||
const origCallback = backendWidget.callback;
|
||||
|
||||
// Override callback to update visibility when backend changes
|
||||
backendWidget.callback = function(value) {
|
||||
log("Backend changed to:", value);
|
||||
|
||||
const result = origCallback?.apply(this, arguments);
|
||||
|
||||
updateWidgetVisibility(value);
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
// Initialize visibility on node creation
|
||||
log("Initializing widget visibility...");
|
||||
updateWidgetVisibility(backendWidget.value);
|
||||
}
|
||||
});
|
||||
|
||||
log("GeomPack TextureToGeometry dynamic parameters extension loaded");
|
||||
@@ -1,250 +0,0 @@
|
||||
/**
|
||||
* ComfyUI-GeometryPack Transform Dynamic Parameter Management
|
||||
*
|
||||
* This extension manages the visibility of operation-specific parameters
|
||||
* in the Transform Mesh node based on the selected operation.
|
||||
*/
|
||||
|
||||
import { app } from "../../../scripts/app.js";
|
||||
|
||||
const DEBUG = false; // Set to true to enable console logging
|
||||
|
||||
function log(...args) {
|
||||
if (DEBUG) {
|
||||
console.log("[GeomPack-Transform-Dynamic]", ...args);
|
||||
}
|
||||
}
|
||||
|
||||
// Helper function to hide a widget (removes from array)
|
||||
function hideWidget(node, widget) {
|
||||
if (!widget) return;
|
||||
if (widget._hidden) {
|
||||
log("Widget already hidden:", widget.name);
|
||||
return;
|
||||
}
|
||||
|
||||
log("Hiding widget:", widget.name);
|
||||
|
||||
const index = node.widgets.indexOf(widget);
|
||||
if (index === -1) {
|
||||
log(" ERROR: Widget not found in array!");
|
||||
return;
|
||||
}
|
||||
|
||||
// Store original properties for restoration
|
||||
if (!widget.origType) {
|
||||
widget.origType = widget.type;
|
||||
widget.origComputeSize = widget.computeSize;
|
||||
widget.origSerializeValue = widget.serializeValue;
|
||||
}
|
||||
|
||||
widget._originalIndex = index;
|
||||
widget._hidden = true;
|
||||
|
||||
// Remove widget from array
|
||||
node.widgets.splice(index, 1);
|
||||
|
||||
log(" Widget removed from index:", index);
|
||||
|
||||
// Hide linked widgets if any
|
||||
if (widget.linkedWidgets) {
|
||||
widget.linkedWidgets.forEach(w => hideWidget(node, w));
|
||||
}
|
||||
}
|
||||
|
||||
// Helper function to show a widget (re-inserts into array)
|
||||
function showWidget(node, widget) {
|
||||
if (!widget) return;
|
||||
if (!widget._hidden) {
|
||||
log("Widget already visible:", widget.name);
|
||||
return;
|
||||
}
|
||||
|
||||
log("Showing widget:", widget.name);
|
||||
|
||||
// Restore original properties
|
||||
if (widget.origType) {
|
||||
widget.type = widget.origType;
|
||||
widget.computeSize = widget.origComputeSize;
|
||||
if (widget.origSerializeValue) {
|
||||
widget.serializeValue = widget.origSerializeValue;
|
||||
}
|
||||
}
|
||||
|
||||
// Re-insert widget at original position
|
||||
const insertIndex = Math.min(widget._originalIndex, node.widgets.length);
|
||||
node.widgets.splice(insertIndex, 0, widget);
|
||||
|
||||
log(" Widget restored to index:", insertIndex);
|
||||
|
||||
widget._hidden = false;
|
||||
|
||||
// Show linked widgets if any
|
||||
if (widget.linkedWidgets) {
|
||||
widget.linkedWidgets.forEach(w => showWidget(node, w));
|
||||
}
|
||||
}
|
||||
|
||||
// Helper to update node size and redraw
|
||||
function updateNodeSize(node) {
|
||||
node.setDirtyCanvas(true, true);
|
||||
if (app.graph) {
|
||||
app.graph.setDirtyCanvas(true, true);
|
||||
}
|
||||
|
||||
requestAnimationFrame(() => {
|
||||
const newSize = node.computeSize();
|
||||
node.setSize([node.size[0], newSize[1]]);
|
||||
node.setDirtyCanvas(true, true);
|
||||
|
||||
if (app.canvas) {
|
||||
app.canvas.setDirty(true, true);
|
||||
}
|
||||
|
||||
requestAnimationFrame(() => {
|
||||
if (app.canvas) {
|
||||
app.canvas.draw(true, true);
|
||||
}
|
||||
log("Widget visibility update complete");
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Main extension registration
|
||||
app.registerExtension({
|
||||
name: "geompack.transform_dynamic_parameters",
|
||||
|
||||
async nodeCreated(node) {
|
||||
// Handle Transform Mesh node
|
||||
if (node.comfyClass === "GeomPackTransformMesh") {
|
||||
setTimeout(() => {
|
||||
this.setupTransformNode(node);
|
||||
}, 100);
|
||||
}
|
||||
},
|
||||
|
||||
setupTransformNode(node) {
|
||||
log("Setting up GeomPackTransformMesh node");
|
||||
|
||||
// Find the operation selector widget
|
||||
const operationWidget = node.widgets?.find(w => w.name === "operation");
|
||||
if (!operationWidget) {
|
||||
log("ERROR: Operation widget not found!");
|
||||
return;
|
||||
}
|
||||
|
||||
log("Operation widget found, current value:", operationWidget.value);
|
||||
|
||||
// Find all parameter widgets
|
||||
const translateXWidget = node.widgets?.find(w => w.name === "translate_x");
|
||||
const translateYWidget = node.widgets?.find(w => w.name === "translate_y");
|
||||
const translateZWidget = node.widgets?.find(w => w.name === "translate_z");
|
||||
|
||||
const rotateXWidget = node.widgets?.find(w => w.name === "rotate_x");
|
||||
const rotateYWidget = node.widgets?.find(w => w.name === "rotate_y");
|
||||
const rotateZWidget = node.widgets?.find(w => w.name === "rotate_z");
|
||||
|
||||
const scaleUniformWidget = node.widgets?.find(w => w.name === "scale_uniform");
|
||||
const scaleXWidget = node.widgets?.find(w => w.name === "scale_x");
|
||||
const scaleYWidget = node.widgets?.find(w => w.name === "scale_y");
|
||||
const scaleZWidget = node.widgets?.find(w => w.name === "scale_z");
|
||||
|
||||
const mirrorAxisWidget = node.widgets?.find(w => w.name === "mirror_axis");
|
||||
|
||||
const centerXWidget = node.widgets?.find(w => w.name === "center_x");
|
||||
const centerYWidget = node.widgets?.find(w => w.name === "center_y");
|
||||
const centerZWidget = node.widgets?.find(w => w.name === "center_z");
|
||||
|
||||
const matrixStringWidget = node.widgets?.find(w => w.name === "matrix_string");
|
||||
|
||||
log("Found parameter widgets:", {
|
||||
translate_x: !!translateXWidget,
|
||||
translate_y: !!translateYWidget,
|
||||
translate_z: !!translateZWidget,
|
||||
rotate_x: !!rotateXWidget,
|
||||
rotate_y: !!rotateYWidget,
|
||||
rotate_z: !!rotateZWidget,
|
||||
scale_uniform: !!scaleUniformWidget,
|
||||
scale_x: !!scaleXWidget,
|
||||
scale_y: !!scaleYWidget,
|
||||
scale_z: !!scaleZWidget,
|
||||
mirror_axis: !!mirrorAxisWidget,
|
||||
center_x: !!centerXWidget,
|
||||
center_y: !!centerYWidget,
|
||||
center_z: !!centerZWidget,
|
||||
matrix_string: !!matrixStringWidget
|
||||
});
|
||||
|
||||
// Function to update widget visibility based on selected operation
|
||||
const updateWidgetVisibility = (selectedOperation) => {
|
||||
log("Updating widget visibility for operation:", selectedOperation);
|
||||
|
||||
// Hide all optional parameters first
|
||||
hideWidget(node, translateXWidget);
|
||||
hideWidget(node, translateYWidget);
|
||||
hideWidget(node, translateZWidget);
|
||||
hideWidget(node, rotateXWidget);
|
||||
hideWidget(node, rotateYWidget);
|
||||
hideWidget(node, rotateZWidget);
|
||||
hideWidget(node, scaleUniformWidget);
|
||||
hideWidget(node, scaleXWidget);
|
||||
hideWidget(node, scaleYWidget);
|
||||
hideWidget(node, scaleZWidget);
|
||||
hideWidget(node, mirrorAxisWidget);
|
||||
hideWidget(node, centerXWidget);
|
||||
hideWidget(node, centerYWidget);
|
||||
hideWidget(node, centerZWidget);
|
||||
hideWidget(node, matrixStringWidget);
|
||||
|
||||
// Show relevant parameters based on operation
|
||||
if (selectedOperation === "translate") {
|
||||
showWidget(node, translateXWidget);
|
||||
showWidget(node, translateYWidget);
|
||||
showWidget(node, translateZWidget);
|
||||
} else if (selectedOperation === "rotate") {
|
||||
showWidget(node, rotateXWidget);
|
||||
showWidget(node, rotateYWidget);
|
||||
showWidget(node, rotateZWidget);
|
||||
} else if (selectedOperation === "scale") {
|
||||
showWidget(node, scaleUniformWidget);
|
||||
showWidget(node, scaleXWidget);
|
||||
showWidget(node, scaleYWidget);
|
||||
showWidget(node, scaleZWidget);
|
||||
} else if (selectedOperation === "mirror") {
|
||||
showWidget(node, mirrorAxisWidget);
|
||||
} else if (selectedOperation === "center") {
|
||||
showWidget(node, centerXWidget);
|
||||
showWidget(node, centerYWidget);
|
||||
showWidget(node, centerZWidget);
|
||||
} else if (selectedOperation === "align_to_axes") {
|
||||
// No parameters needed - all hidden
|
||||
} else if (selectedOperation === "apply_matrix") {
|
||||
showWidget(node, matrixStringWidget);
|
||||
}
|
||||
|
||||
updateNodeSize(node);
|
||||
};
|
||||
|
||||
// Store original callback
|
||||
const origCallback = operationWidget.callback;
|
||||
|
||||
// Override callback to update visibility when operation changes
|
||||
operationWidget.callback = function(value) {
|
||||
log("Operation changed to:", value);
|
||||
|
||||
// Call original callback if it exists
|
||||
const result = origCallback?.apply(this, arguments);
|
||||
|
||||
// Update widget visibility
|
||||
updateWidgetVisibility(value);
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
// Initialize visibility on node creation
|
||||
log("Initializing widget visibility...");
|
||||
updateWidgetVisibility(operationWidget.value);
|
||||
}
|
||||
});
|
||||
|
||||
log("GeomPack transform dynamic parameters extension loaded");
|
||||
@@ -1,214 +0,0 @@
|
||||
/**
|
||||
* ComfyUI-GeometryPack UV Unwrap Dynamic Parameter Management
|
||||
*
|
||||
* This extension manages the visibility of method-specific parameters
|
||||
* in the UV Unwrap node based on the selected method.
|
||||
*/
|
||||
|
||||
import { app } from "../../../scripts/app.js";
|
||||
|
||||
const DEBUG = false; // Set to true to enable console logging
|
||||
|
||||
function log(...args) {
|
||||
if (DEBUG) {
|
||||
console.log("[GeomPack-UVUnwrap]", ...args);
|
||||
}
|
||||
}
|
||||
|
||||
// Helper function to hide a widget (removes from array)
|
||||
function hideWidget(node, widget) {
|
||||
if (!widget) return;
|
||||
if (widget._hidden) {
|
||||
log("Widget already hidden:", widget.name);
|
||||
return;
|
||||
}
|
||||
|
||||
log("Hiding widget:", widget.name);
|
||||
|
||||
const index = node.widgets.indexOf(widget);
|
||||
if (index === -1) {
|
||||
log(" ERROR: Widget not found in array!");
|
||||
return;
|
||||
}
|
||||
|
||||
// Store original properties for restoration
|
||||
if (!widget.origType) {
|
||||
widget.origType = widget.type;
|
||||
widget.origComputeSize = widget.computeSize;
|
||||
widget.origSerializeValue = widget.serializeValue;
|
||||
}
|
||||
|
||||
widget._originalIndex = index;
|
||||
widget._hidden = true;
|
||||
|
||||
// Remove widget from array
|
||||
node.widgets.splice(index, 1);
|
||||
|
||||
log(" Widget removed from index:", index);
|
||||
|
||||
// Hide linked widgets if any
|
||||
if (widget.linkedWidgets) {
|
||||
widget.linkedWidgets.forEach(w => hideWidget(node, w));
|
||||
}
|
||||
}
|
||||
|
||||
// Helper function to show a widget (re-inserts into array)
|
||||
function showWidget(node, widget) {
|
||||
if (!widget) return;
|
||||
if (!widget._hidden) {
|
||||
log("Widget already visible:", widget.name);
|
||||
return;
|
||||
}
|
||||
|
||||
log("Showing widget:", widget.name);
|
||||
|
||||
// Restore original properties
|
||||
if (widget.origType) {
|
||||
widget.type = widget.origType;
|
||||
widget.computeSize = widget.origComputeSize;
|
||||
if (widget.origSerializeValue) {
|
||||
widget.serializeValue = widget.origSerializeValue;
|
||||
}
|
||||
}
|
||||
|
||||
// Re-insert widget at original position
|
||||
const insertIndex = Math.min(widget._originalIndex, node.widgets.length);
|
||||
node.widgets.splice(insertIndex, 0, widget);
|
||||
|
||||
log(" Widget restored to index:", insertIndex);
|
||||
|
||||
widget._hidden = false;
|
||||
|
||||
// Show linked widgets if any
|
||||
if (widget.linkedWidgets) {
|
||||
widget.linkedWidgets.forEach(w => showWidget(node, w));
|
||||
}
|
||||
}
|
||||
|
||||
// Helper to update node size and redraw
|
||||
function updateNodeSize(node) {
|
||||
node.setDirtyCanvas(true, true);
|
||||
if (app.graph) {
|
||||
app.graph.setDirtyCanvas(true, true);
|
||||
}
|
||||
|
||||
requestAnimationFrame(() => {
|
||||
const newSize = node.computeSize();
|
||||
node.setSize([node.size[0], newSize[1]]);
|
||||
node.setDirtyCanvas(true, true);
|
||||
|
||||
if (app.canvas) {
|
||||
app.canvas.setDirty(true, true);
|
||||
}
|
||||
|
||||
requestAnimationFrame(() => {
|
||||
if (app.canvas) {
|
||||
app.canvas.draw(true, true);
|
||||
}
|
||||
log("Widget visibility update complete");
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Main extension registration
|
||||
app.registerExtension({
|
||||
name: "geompack.uv_unwrap_dynamic",
|
||||
|
||||
async nodeCreated(node) {
|
||||
// Handle UV Unwrap node
|
||||
if (node.comfyClass === "GeomPackUVUnwrap") {
|
||||
setTimeout(() => {
|
||||
this.setupUVUnwrapNode(node);
|
||||
}, 100);
|
||||
}
|
||||
},
|
||||
|
||||
setupUVUnwrapNode(node) {
|
||||
log("Setting up GeomPackUVUnwrap node");
|
||||
|
||||
// Find the method selector widget
|
||||
const methodWidget = node.widgets?.find(w => w.name === "method");
|
||||
if (!methodWidget) {
|
||||
log("ERROR: Method widget not found!");
|
||||
return;
|
||||
}
|
||||
|
||||
log("Method widget found, current value:", methodWidget.value);
|
||||
|
||||
// Find all parameter widgets
|
||||
const iterationsWidget = node.widgets?.find(w => w.name === "iterations");
|
||||
const angleLimitWidget = node.widgets?.find(w => w.name === "angle_limit");
|
||||
const islandMarginWidget = node.widgets?.find(w => w.name === "island_margin");
|
||||
const scaleToBoundsWidget = node.widgets?.find(w => w.name === "scale_to_bounds");
|
||||
const cubeSizeWidget = node.widgets?.find(w => w.name === "cube_size");
|
||||
const cylinderRadiusWidget = node.widgets?.find(w => w.name === "cylinder_radius");
|
||||
|
||||
log("Found parameter widgets:", {
|
||||
iterations: !!iterationsWidget,
|
||||
angle_limit: !!angleLimitWidget,
|
||||
island_margin: !!islandMarginWidget,
|
||||
scale_to_bounds: !!scaleToBoundsWidget,
|
||||
cube_size: !!cubeSizeWidget,
|
||||
cylinder_radius: !!cylinderRadiusWidget
|
||||
});
|
||||
|
||||
// Function to update widget visibility based on selected method
|
||||
const updateWidgetVisibility = (selectedMethod) => {
|
||||
log("Updating widget visibility for method:", selectedMethod);
|
||||
|
||||
// Hide all optional parameters first
|
||||
hideWidget(node, iterationsWidget);
|
||||
hideWidget(node, angleLimitWidget);
|
||||
hideWidget(node, islandMarginWidget);
|
||||
hideWidget(node, scaleToBoundsWidget);
|
||||
hideWidget(node, cubeSizeWidget);
|
||||
hideWidget(node, cylinderRadiusWidget);
|
||||
|
||||
// Show relevant parameters based on method
|
||||
if (selectedMethod === "xatlas" ||
|
||||
selectedMethod === "libigl_lscm" ||
|
||||
selectedMethod === "libigl_harmonic") {
|
||||
// No parameters for these methods
|
||||
log(" No parameters needed for", selectedMethod);
|
||||
} else if (selectedMethod === "libigl_arap") {
|
||||
showWidget(node, iterationsWidget);
|
||||
} else if (selectedMethod === "blender_smart") {
|
||||
showWidget(node, angleLimitWidget);
|
||||
showWidget(node, islandMarginWidget);
|
||||
showWidget(node, scaleToBoundsWidget);
|
||||
} else if (selectedMethod === "blender_cube") {
|
||||
showWidget(node, cubeSizeWidget);
|
||||
showWidget(node, scaleToBoundsWidget);
|
||||
} else if (selectedMethod === "blender_cylinder") {
|
||||
showWidget(node, cylinderRadiusWidget);
|
||||
showWidget(node, scaleToBoundsWidget);
|
||||
} else if (selectedMethod === "blender_sphere") {
|
||||
showWidget(node, scaleToBoundsWidget);
|
||||
}
|
||||
|
||||
updateNodeSize(node);
|
||||
};
|
||||
|
||||
// Store original callback
|
||||
const origCallback = methodWidget.callback;
|
||||
|
||||
// Override callback to update visibility when method changes
|
||||
methodWidget.callback = function(value) {
|
||||
log("Method changed to:", value);
|
||||
|
||||
// Call original callback if it exists
|
||||
const result = origCallback?.apply(this, arguments);
|
||||
|
||||
// Update widget visibility
|
||||
updateWidgetVisibility(value);
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
// Initialize visibility on node creation
|
||||
log("Initializing widget visibility...");
|
||||
updateWidgetVisibility(methodWidget.value);
|
||||
}
|
||||
});
|
||||
|
||||
log("GeomPack UV Unwrap dynamic parameters extension loaded");
|
||||
Reference in New Issue
Block a user