two venvs

This commit is contained in:
Andrea Pozzetti
2026-02-13 00:41:40 +01:00
parent 950ed0c58f
commit d8937c5e46
22 changed files with 76 additions and 289 deletions
-147
View File
@@ -3,9 +3,7 @@
import os
import sys
import shutil
from pathlib import Path
from datetime import datetime
print("[geompack] loading...", file=sys.stderr, flush=True)
from comfy_env import register_nodes
@@ -36,150 +34,5 @@ def _generate_widget_mappings():
_generate_widget_mappings()
# Server routes
try:
from aiohttp import web
from server import PromptServer
import folder_paths
routes = PromptServer.instance.routes
@routes.post("/geometrypack/save_preview")
async def save_preview_mesh(request):
try:
json_data = await request.json()
temp_filename = json_data.get("temp_filename")
if not temp_filename:
return web.json_response({"success": False, "error": "No temp_filename"}, status=400)
output_dir = folder_paths.get_output_directory()
temp_filepath = os.path.join(output_dir, temp_filename)
if not os.path.exists(temp_filepath):
return web.json_response({"success": False, "error": "File not found"}, status=404)
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
file_ext = os.path.splitext(temp_filename)[1]
saved_filename = f"mesh_{timestamp}{file_ext}"
shutil.copy2(temp_filepath, os.path.join(output_dir, saved_filename))
return web.json_response({"success": True, "saved_filename": saved_filename})
except Exception as e:
return web.json_response({"success": False, "error": str(e)}, status=500)
@routes.post("/geompack/analyze")
async def analyze_mesh(request):
try:
from .nodes.visualization.preview_mesh_analysis import (
get_cached_mesh, add_field_to_cached_mesh,
compute_boundary_vertices, compute_connected_components,
compute_self_intersections
)
from .nodes.visualization._vtp_export import export_mesh_with_scalars_vtp
json_data = await request.json()
mesh_id = json_data.get("mesh_id")
analysis_type = json_data.get("analysis_type")
if not mesh_id or not analysis_type:
return web.json_response({"success": False, "error": "Missing params"}, status=400)
cache_entry = get_cached_mesh(mesh_id)
if not cache_entry:
return web.json_response({"success": False, "error": "Mesh not found"}, status=404)
mesh = cache_entry['mesh']
filename = cache_entry['filename']
if analysis_type == "open_edges":
mesh, count = compute_boundary_vertices(mesh)
field_name = "boundary_vertex"
elif analysis_type == "components":
mesh, count = compute_connected_components(mesh)
field_name = "face.part_id"
elif analysis_type == "self_intersect":
mesh, count = compute_self_intersections(mesh)
field_name = "face.self_intersect"
else:
return web.json_response({"success": False, "error": f"Unknown: {analysis_type}"}, status=400)
add_field_to_cached_mesh(mesh_id, field_name)
filepath = os.path.join(folder_paths.get_output_directory(), filename)
export_mesh_with_scalars_vtp(mesh, filepath)
return web.json_response({"success": True, "filename": filename, "field_name": field_name, "count": count})
except Exception as e:
return web.json_response({"success": False, "error": str(e)}, status=500)
@routes.post("/geompack/find_location")
async def find_location(request):
try:
from .nodes.visualization.preview_mesh_analysis import get_cached_mesh
import re
json_data = await request.json()
mesh_id = json_data.get("mesh_id")
query = json_data.get("query", "").strip()
if not mesh_id or not query:
return web.json_response({"success": False, "error": "Missing params"}, status=400)
cache_entry = get_cached_mesh(mesh_id)
if not cache_entry:
return web.json_response({"success": False, "error": "Mesh not found"}, status=404)
mesh = cache_entry['mesh']
point, query_type, element_id = None, None, None
# Face ID: f123, face123
face_match = re.match(r'^[fF](?:ace)?\s*(\d+)$', query)
if face_match:
face_id = int(face_match.group(1))
if 0 <= face_id < len(mesh.faces):
point = mesh.vertices[mesh.faces[face_id]].mean(axis=0).tolist()
query_type, element_id = "face", face_id
# Vertex ID: v123, vertex123
if point is None:
vertex_match = re.match(r'^[vVpP](?:ertex|oint)?\s*(\d+)$', query)
if vertex_match:
vertex_id = int(vertex_match.group(1))
if 0 <= vertex_id < len(mesh.vertices):
point = mesh.vertices[vertex_id].tolist()
query_type, element_id = "vertex", vertex_id
# XYZ coordinates
if point is None:
coord_str = query.strip('()[]')
parts = re.split(r'[,\s]+', coord_str)
if len(parts) == 3:
try:
point = [float(parts[0]), float(parts[1]), float(parts[2])]
query_type = "coordinate"
except ValueError:
pass
# Plain number = face ID
if point is None:
try:
face_id = int(query)
if 0 <= face_id < len(mesh.faces):
point = mesh.vertices[mesh.faces[face_id]].mean(axis=0).tolist()
query_type, element_id = "face", face_id
except ValueError:
pass
if point is None:
return web.json_response({"success": False, "error": f"Could not parse: {query}"}, status=400)
response = {"success": True, "point": point, "type": query_type}
if element_id is not None:
response["id"] = element_id
return web.json_response(response)
except Exception as e:
return web.json_response({"success": False, "error": str(e)}, status=500)
except Exception:
pass
WEB_DIRECTORY = "./web"
__all__ = ['NODE_CLASS_MAPPINGS', 'NODE_DISPLAY_NAME_MAPPINGS', 'WEB_DIRECTORY']
+1 -9
View File
@@ -6,12 +6,4 @@ OMP_NUM_THREADS = 1
# Include both old and new package names for compatibility across Ubuntu versions
# (libgl1-mesa-glx was renamed to libgl1 in Ubuntu 22.04+)
# libopengl0 provides libOpenGL.so.0 (required by pymeshlab)
packages = ["libgl1-mesa-glx", "libgl1", "libopengl0", "libglu1-mesa"]
# Linux-specific pypi packages
[target.linux-64.pypi-dependencies]
embreex = "*"
# Windows-specific packages
[target.win-64.pypi-dependencies]
embreex = "*"
packages = ["libgl1-mesa-glx", "libgl1", "libopengl0", "libglu1-mesa"]
-29
View File
@@ -1,29 +0,0 @@
# SPDX-License-Identifier: GPL-3.0-or-later
# Copyright (C) 2025 ComfyUI-GeometryPack Contributors
"""
CGAL-dependent nodes - requires igl.copyleft.cgal or CGAL Python bindings.
These nodes will only load if CGAL dependencies are available.
"""
# Import submodules
from . import repair
from . import boolean
from . import remeshing
from . import reconstruction
# Collect all node class mappings
NODE_CLASS_MAPPINGS = {}
NODE_CLASS_MAPPINGS.update(repair.NODE_CLASS_MAPPINGS)
NODE_CLASS_MAPPINGS.update(boolean.NODE_CLASS_MAPPINGS)
NODE_CLASS_MAPPINGS.update(remeshing.NODE_CLASS_MAPPINGS)
NODE_CLASS_MAPPINGS.update(reconstruction.NODE_CLASS_MAPPINGS)
# Collect all display name mappings
NODE_DISPLAY_NAME_MAPPINGS = {}
NODE_DISPLAY_NAME_MAPPINGS.update(repair.NODE_DISPLAY_NAME_MAPPINGS)
NODE_DISPLAY_NAME_MAPPINGS.update(boolean.NODE_DISPLAY_NAME_MAPPINGS)
NODE_DISPLAY_NAME_MAPPINGS.update(remeshing.NODE_DISPLAY_NAME_MAPPINGS)
NODE_DISPLAY_NAME_MAPPINGS.update(reconstruction.NODE_DISPLAY_NAME_MAPPINGS)
__all__ = ['NODE_CLASS_MAPPINGS', 'NODE_DISPLAY_NAME_MAPPINGS']
-19
View File
@@ -1,19 +0,0 @@
[apt]
packages = ["libgl1-mesa-glx", "libgl1", "libopengl0", "libglu1-mesa"]
[dependencies]
cgal = "*"
[pypi-dependencies]
numpy = "*"
trimesh = { version = "*", extras = ["easy"] }
pymeshlab = ">=2025.7"
libigl = "*"
# Windows needs Visual C++ runtime for CGAL DLLs
[target.win-64.pypi-dependencies]
msvc-runtime = "*"
[env_vars]
KMP_DUPLICATE_LIB_OK = "TRUE"
OMP_NUM_THREADS = 1
-12
View File
@@ -1,12 +0,0 @@
# Import submodules
from . import remesh
# Collect all node class mappings
NODE_CLASS_MAPPINGS = {}
NODE_CLASS_MAPPINGS.update(remesh.NODE_CLASS_MAPPINGS)
# Collect all display name mappings
NODE_DISPLAY_NAME_MAPPINGS = {}
NODE_DISPLAY_NAME_MAPPINGS.update(remesh.NODE_DISPLAY_NAME_MAPPINGS)
__all__ = ['NODE_CLASS_MAPPINGS', 'NODE_DISPLAY_NAME_MAPPINGS']
-7
View File
@@ -1,7 +0,0 @@
[cuda]
packages = ["cumesh", "torch", "torchvision"]
[pypi-dependencies]
numpy = "*"
trimesh = { version = "*", extras = ["easy"] }
tqdm = "*"
+21 -2
View File
@@ -2,8 +2,8 @@
# Copyright (C) 2025 ComfyUI-GeometryPack Contributors
"""
Main nodes - Basic dependencies (trimesh, numpy, pymeshlab, etc.)
These nodes will always load regardless of CGAL or Blender availability.
Main nodes - All non-Blender geometry processing nodes.
Runs in an isolation env (pixi/conda) to avoid DLL conflicts with ComfyUI's torch.
"""
from . import io
@@ -21,6 +21,15 @@ from . import remeshing
from . import reconstruction
from . import texture_remeshing
# CGAL nodes (moved from nodes/cgal/)
from . import boolean
from . import reconstruction_cgal
from . import remeshing_cgal
from . import repair_cgal
# GPU nodes (moved from nodes/gpu/)
from . import remeshing_gpu
# Collect all node class mappings
NODE_CLASS_MAPPINGS = {}
NODE_CLASS_MAPPINGS.update(io.NODE_CLASS_MAPPINGS)
@@ -37,6 +46,11 @@ NODE_CLASS_MAPPINGS.update(repair.NODE_CLASS_MAPPINGS)
NODE_CLASS_MAPPINGS.update(remeshing.NODE_CLASS_MAPPINGS)
NODE_CLASS_MAPPINGS.update(reconstruction.NODE_CLASS_MAPPINGS)
NODE_CLASS_MAPPINGS.update(texture_remeshing.NODE_CLASS_MAPPINGS)
NODE_CLASS_MAPPINGS.update(boolean.NODE_CLASS_MAPPINGS)
NODE_CLASS_MAPPINGS.update(reconstruction_cgal.NODE_CLASS_MAPPINGS)
NODE_CLASS_MAPPINGS.update(remeshing_cgal.NODE_CLASS_MAPPINGS)
NODE_CLASS_MAPPINGS.update(repair_cgal.NODE_CLASS_MAPPINGS)
NODE_CLASS_MAPPINGS.update(remeshing_gpu.NODE_CLASS_MAPPINGS)
# Collect all display name mappings
NODE_DISPLAY_NAME_MAPPINGS = {}
@@ -54,5 +68,10 @@ NODE_DISPLAY_NAME_MAPPINGS.update(repair.NODE_DISPLAY_NAME_MAPPINGS)
NODE_DISPLAY_NAME_MAPPINGS.update(remeshing.NODE_DISPLAY_NAME_MAPPINGS)
NODE_DISPLAY_NAME_MAPPINGS.update(reconstruction.NODE_DISPLAY_NAME_MAPPINGS)
NODE_DISPLAY_NAME_MAPPINGS.update(texture_remeshing.NODE_DISPLAY_NAME_MAPPINGS)
NODE_DISPLAY_NAME_MAPPINGS.update(boolean.NODE_DISPLAY_NAME_MAPPINGS)
NODE_DISPLAY_NAME_MAPPINGS.update(reconstruction_cgal.NODE_DISPLAY_NAME_MAPPINGS)
NODE_DISPLAY_NAME_MAPPINGS.update(remeshing_cgal.NODE_DISPLAY_NAME_MAPPINGS)
NODE_DISPLAY_NAME_MAPPINGS.update(repair_cgal.NODE_DISPLAY_NAME_MAPPINGS)
NODE_DISPLAY_NAME_MAPPINGS.update(remeshing_gpu.NODE_DISPLAY_NAME_MAPPINGS)
__all__ = ['NODE_CLASS_MAPPINGS', 'NODE_DISPLAY_NAME_MAPPINGS']
+39
View File
@@ -0,0 +1,39 @@
[apt]
packages = ["libgl1-mesa-glx", "libgl1", "libopengl0", "libglu1-mesa"]
[dependencies]
cgal = "*"
[cuda]
packages = ["cumesh", "torch", "torchvision"]
[pypi-dependencies]
numpy = "*"
scipy = ">=1.7.0"
tqdm = ">=4.60.0"
requests = ">=2.25.0"
trimesh = { version = ">=4.0.0", extras = ["easy"] }
libigl = ">=2.6.1"
pymeshlab = ">=2025.7"
rtree = "*"
point-cloud-utils = ">=0.30.0"
mesh-to-sdf = ">=0.0.14"
fast-simplification = ">=0.1.5"
pymeshfix = ">=0.17.0"
xatlas = ">=0.0.11"
skeletor = ">=1.2.0"
pynanoinstantmeshes = "*"
matplotlib = ">=3.5.0"
pillow = ">=9.0.0"
pyvista = ">=0.43.0"
[target.win-64.pypi-dependencies]
embreex = "*"
msvc-runtime = "*"
[target.linux-64.pypi-dependencies]
embreex = "*"
[env_vars]
KMP_DUPLICATE_LIB_OK = "TRUE"
OMP_NUM_THREADS = 1
+10
View File
@@ -0,0 +1,10 @@
# Import submodules
from .remesh import NODE_CLASS_MAPPINGS as REMESH_MAPS, NODE_DISPLAY_NAME_MAPPINGS as REMESH_DISP
NODE_CLASS_MAPPINGS = {}
NODE_DISPLAY_NAME_MAPPINGS = {}
NODE_CLASS_MAPPINGS.update(REMESH_MAPS)
NODE_DISPLAY_NAME_MAPPINGS.update(REMESH_DISP)
__all__ = ['NODE_CLASS_MAPPINGS', 'NODE_DISPLAY_NAME_MAPPINGS']
-36
View File
@@ -9,42 +9,6 @@ from comfy_3d_viewers import copy_viewer
setup_env()
# ---------------------------------------------------------------------------
# Crash diagnostics: install hooks that persist AFTER prestartup completes.
# These help identify which ComfyUI import causes the process to crash.
# Only active when COMFY_ENV_DEBUG=1 (set in CI).
# ---------------------------------------------------------------------------
if os.environ.get("COMFY_ENV_DEBUG", "").lower() in ("1", "true", "yes"):
import atexit
def _exit_hook():
print("[geompack] atexit handler called — clean Python shutdown",
file=sys.stderr, flush=True)
atexit.register(_exit_hook)
_orig_excepthook = sys.excepthook
def _crash_excepthook(exc_type, exc_value, exc_tb):
print(f"[geompack] UNHANDLED: {exc_type.__name__}: {exc_value}",
file=sys.stderr, flush=True)
_orig_excepthook(exc_type, exc_value, exc_tb)
sys.excepthook = _crash_excepthook
class _ImportTracer:
"""Log first-time imports of key modules to identify crash location."""
_PREFIXES = ('comfy', 'execution', 'server', 'protocol', 'nodes',
'torch', 'comfyui_version', 'hook_breaker', 'comfy_aimdo',
'app', 'cuda_malloc')
def find_spec(self, fullname, path=None, target=None):
if fullname not in sys.modules:
parts = fullname.split('.')
if parts[0] in self._PREFIXES and len(parts) <= 2:
print(f"[import-trace] {fullname}",
file=sys.stderr, flush=True)
return None # never handle — let real finders do the work
sys.meta_path.insert(0, _ImportTracer())
SCRIPT_DIR = Path(__file__).resolve().parent
COMFYUI_DIR = SCRIPT_DIR.parent.parent
+5 -28
View File
@@ -1,30 +1,7 @@
# Core
comfy-env==0.1.63
scipy>=1.7.0
tqdm>=4.60.0
requests>=2.25.0
# Mesh processing
trimesh>=4.0.0
libigl>=2.6.1
pymeshlab>=2025.7
rtree
point-cloud-utils>=0.30.0
mesh-to-sdf>=0.0.14
fast-simplification>=0.1.5
pymeshfix>=0.17.0
xatlas>=0.0.11
skeletor>=1.2.0
PyNanoInstantMeshes
# Visualization
matplotlib>=3.5.0
Pillow>=9.0.0
pyvista>=0.43.0
# ComfyUI integration
# Main process deps (prestartup + lightweight shared libs)
comfy-env==0.1.64
comfy-dynamic-widgets==0.1.1
comfy-3d-viewers==0.2.2
# Platform-specific
embreex; sys_platform != "darwin"
numpy
trimesh>=4.0.0
scipy>=1.7.0