3D clip view + orthographic pick view (XY/YZ/XZ toggle): click 2 points to
define a cut line swept along the view axis into a clip plane; flip clip
slices the other side. Python node exports the mesh .vtp; clipping is
interactive in viewer_slicer.html. prestartup copies the new slicer viewer.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add pmp_target_vertices (0 = off) on the backend + Remesh node pmp_uniform
option. When set, back-solves the target edge length from the input mesh
area for a triangle mesh: V ~= 2A/(sqrt(3) e^2) -> e = sqrt(2A/(sqrt(3) V)).
Overrides pmp_edge_length. Approximate.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add optional cleanup passes (run on the simplified mesh, before read):
remove_degenerate_faces, remove_duplicate_faces, repair_non_manifold_edges,
remove_non_manifold_faces, remove_small_components_min_area (0=off),
remove_unreferenced_vertices. Each guarded; applied per-face removals ->
small components -> orphan verts last. Exposed on backend + Remesh node
gpu_cumesh option (same names, no remap).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The per-process-EGL GPU pool fell back to vtkXOpenGLRenderWindow with no
DISPLAY ("bad X server connection") and ran ~12s/mesh -- worse than
serial. Gate the process pool back to the software path only; GPU renders
per-mesh on the serial path (fresh plotter -> shading). Software CPU pool
and the shading fix are kept.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
grid_resolution (DC voxel grid, was hardcoded 512) is the main detail/
speed knob; project_back re-projects DC verts onto the input surface for
fidelity (was hardcoded 0). Exposed on the backend node and the Remesh
node gpu_cumesh option (gpu_grid_resolution/gpu_project_back -> mapped).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
QuadWild only exposes scale_factor (no count/edge input). Add
qw_target_vertices and qw_target_edge_length (both 0 = off) on the
QuadWild backend + the Remesh node UI. Back-solve scale_factor from the
input mesh area: vertices -> edge via e=sqrt(area/N), edge -> scale via
scale = e / mean_input_edge. Priority edge_length > vertices > scale_factor.
Approximate (QuadWild is edge-length-driven, not count-exact).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Revert plotter reuse (p.clear() dropped lights -> flat renders; reuse
barely helped anyway). Fresh plotter per mesh restores shading.
- Parallelise BOTH gpu and software paths across max CPU cores via a
process pool: each worker renders in its own GL context (gpu children
unset DISPLAY -> per-process EGL; software children share parent Xvfb)
and returns the small image, not the mesh. This is the Lightning-style
(processes, not threads) parallelism -- parallelises the CPU-bound prep
while the shared GPU serialises the cheap rasterisation. Serial fallback.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
pv.PolyData(points) adds one VERT cell per point, so n_cells became
verts+lines+polys and cell_data (lines+polys only) mismatched. Build the
PolyData empty then assign points/faces/lines -> no verts.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
edge_value = reduce(face_field[A], face_field[B]); face_normals+angle =
dihedral. Thresholded edges + surface written to an ASCII .vtp and shown
in the shared VTK.js viewer (viewer_vtk.html) in-node. Face field is a
string (face_normals or any face_attributes key; available ones logged).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Recreating the EGL render window per mesh dominated per-mesh time. Keep a
single plotter alive and clear()+redraw between meshes (recreate only
after a failure). Refactored render into _make_plotter/_prep_poly/
_draw_into/_screenshot_arr; titles use named add_text so they replace on
reuse. No decimation. Workers (software path) still one-shot.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The Y/Z swap is a reflection: deep-copy the wrapped mesh (never mutate the
upstream trimesh), flip_normals to restore winding/lighting, and call
reset_camera so the view re-frames the transformed bounds.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Lazy-imported comfy.utils.ProgressBar, advanced per mesh in both the
process-pool and serial paths.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Fix: len(m.faces or []) raised "ambiguous truth value" on the numpy
faces array, crashing the whole load. Use a safe _n_faces() helper.
- Threads barely helped (trimesh OBJ parsing is GIL-bound Python); switch
to a ProcessPoolExecutor for true CPU-parallel parsing, with a serial
fallback on error.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
When use_gpu is off and >1 mesh, render across CPU cores via a process
pool (software rasterisation is embarrassingly parallel). Parent starts
Xvfb without a GL context (fork-safe); workers reload from file_path when
available to avoid pickling big meshes. Shared _render_mesh_array used by
serial + worker paths. GPU path unchanged.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
use_multithreading toggle loads files via a ThreadPoolExecutor (workers =
cpu_count); mesh I/O + numpy OBJ parsing release the GIL so big batches
load much faster. Order preserved; live per-mesh logging.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- revert_yz_flip: swap Y/Z axes
- three_plane: 2x2 grid (iso(random) + XY/YZ/XZ)
- mesh_opacity + show_edges with conditional edge_opacity (hidden until edges on, via web_src JS)
- show_connected_components: color by pyvista connectivity() RegionId
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Upgrade the batch renderer (was Render Mesh Batch): optional GPU/native
EGL offscreen with software fallback, tqdm + ComfyUI progress bars, and
the filename drawn via plotter.add_title. Renamed to Preview Mesh Batch
Render (node_id GeomPackPreviewMeshBatchRender).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Native <datalist> never opens inside ComfyUI transformed DOM widgets;
replace with a body-attached floating dropdown (fixed-positioned under the
input), click-to-descend into folders, fed by /gpack/getpath.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Inspired by VHS Load Video (Path), but GeometryPack-owned: register
/gpack/getpath in __init__ (main process, after PromptServer exists) to
list directory contents, and a web_src widget that replaces folder_path
with an <input>+<datalist> showing live FOLDER suggestions.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The Three.js viewer.html OBJLoader chokes on some OBJs; viewer_vtk.html
uses VTK readers (same LOAD_MESH protocol) and shows the download bar.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Just-uploaded meshes are added to the file_path combo client-side but are
not in the backend cached INPUT_TYPES options, so queueing hit a 400
"Value not in list". validate_inputs(**kwargs)->True skips that combo
check; execute() still resolves the path and raises if truly missing.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Switch upload from fetch to XHR for upload progress, show a bar on the
node during upload. Top-level log to confirm the extension loads.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Load Mesh: drop/upload meshes (reuses /upload/image, subfolder=3d, like
the official Load Image) + a "view 3d" toggle that mounts the shared
vtk.js viewer inline. Frontend source in tracked web_src/, copied to the
generated web/js at prestartup.
- Render Mesh Batch: offscreen pyvista render per mesh, titled by name.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Deletes mesh faces/points by a scalar field value via pyvista's threshold filter,
reusing the existing trimesh<->pyvista bridge. Keep modes above/below/between/equal,
on vertex (point) or face (cell) data (auto-prefers face data), with all_scalars for
point fields. E.g. field='viewed', keep='above', value=0.5 -> keep only the faces
Pixal3D's view actually saw. Output mesh retains surviving fields + a summary.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>