Laser Cutter Path Tracer
Plotting the shortest laser-head route through every hole and slot in your part
- image
- traced_image
- feature_count
- path_info
When a laser cutter has to cut a part with forty holes, the machine usually has to be told what order to visit them in - and a naive order wastes a lot of head travel. Laser Cutter Path Tracer looks at a black-and-white cutting diagram, finds every internal feature (holes, slots, cutouts, engravings), and solves the "which order to cut them" problem as a traveling-salesman route from the bottom-right corner to the bottom-left. It's the node that turns a static drawing into an actual cutting plan.
How it works
First it detects internal features in the binarized image: contours that aren't the outer boundary, classified by geometry (holes by circularity, slots by aspect ratio, rectangles by vertex count, complex shapes by solidity). The detection pipeline is the same "robust" machinery the pack's HoleCounter uses - threshold, morphological close to bridge gaps, area filtering, dedup of near-identical features - with a handful of tuning knobs:
binary_threshold(default 128) - binarization cutoff.min_feature_area(default 30 px²) - ignore specks smaller than this.max_feature_area_pct(default 0.4) - drop anything bigger than 40% of the outer boundary (i.e. the outer boundary itself or huge blobs).morph_close_size(default 5) - bridging kernel for broken outlines; 0 disables.morph_dilate_size(default 0) - thickens thin strokes; bump to 2–3 for faint scans.dedup_radius(default 15) - merge features whose centers are within this many pixels.use_adaptive_threshold(default off) - for noisy or unevenly-lit scans; bringsadaptive_block_sizeandadaptive_cinto play.detect_open_contours(default on) - also catches arcs, grooves, and other open line segments, not just closed holes.
Then it computes each feature's centroid and solves a TSP path from the start (bottom-right, with a 2% margin) through every feature to the end (bottom-left), using nearest-neighbor-plus refinement. The result is drawn as a line from feature to feature over the original image.
What comes out
traced_image(IMAGE) - the drawing with the optimized head path overlaid.feature_count(INT) - how many features the path visits.path_info(STRING) - a full text report: feature breakdown by type, total path length in pixels, and the numbered visit order with each feature's coordinates. Wire this to Show Text and you've got a cut plan you can actually follow or export.
If nothing is detected, it doesn't crash - it returns the image unchanged and a path_info starting with STATUS: BYPASS, plus troubleshooting hints (toggle invert_image, lower min_feature_area, enable adaptive threshold).
Where it fits
Load Image → Extract Black → ContourGapCloser (if contours are broken) → LaserPathTracer. Clean closed contours matter - the tracer detects features by their boundaries, so run the gap closer first if your drawing has breaks. The output is a planning aid: it gives you the route and visit order; the actual G-code still lives in your CAM software.
Install
Part of ComfyUI-HappNodeSet (mikemojen). ComfyUI Manager: search HappNodeSet. Manual:
cd ComfyUI/custom_nodes
git clone https://github.com/mikemojen/ComfyUI-HappNodeSet.git
pip install -r ComfyUI-HappNodeSet/requirements.txt
Restart ComfyUI. OpenCV does the contour work; numpy the geometry; no exotic deps beyond the pack standard.
Honest caveats
The path is a heuristic TSP, not a proven optimum - it's a good route, usually a very good one, but on pathological layouts (dozens of near-collinear holes) you might save a bit more by hand. And it traces feature centers for the travel path, not the cutting path itself - that's a head-movement plan, which is exactly what most operators actually want. Preview the traced image once and read the path_info; if the feature count looks wrong, fix detection before trusting the route.
Inputs (12)
| Name | Type | Default | Description |
|---|---|---|---|
| image | IMAGE | — | |
| binary_threshold | INT | 1280–255 | — |
| min_feature_area | INT | 301–50000 | Minimum contour area (px2) to count as a feature. |
| max_feature_area_pct | FLOAT | 0.400.01–0.99 | Max feature area as fraction of external boundary. |
| morph_close_size | INT | 50–31 | Morphological close kernel. Bridges gaps. 0=off. |
| morph_dilate_size | INT | 00–15 | Dilation kernel. Thickens thin strokes. 0=off. |
| dedup_radius | INT | 150–100 | Merge features whose centers are within this many px. |
| invert_image | BOOLEAN | false | — |
| use_adaptive_threshold | BOOLEAN | false | Adaptive thresholding for noisy/low-contrast images. |
| adaptive_block_size | INT | 513–201 | — |
| adaptive_c | INT | 10-30–60 | — |
| detect_open_contours | BOOLEAN | true | Also detect arcs, grooves, engravings (open line segments). |
Outputs (3)
| Name | Type | Description |
|---|---|---|
| traced_image | IMAGE | — |
| feature_count | INT | — |
| path_info | STRING | — |