Advanced Shape Placement
150 non-overlapping shapes in seconds — the collage node that does the fiddly work
- shape_mask
- region_mask
- base_mask
- exclusion_mask
- MASK (all)
- MASK (new)
- JSON
Place shapes by hand and you'll spend the evening wiring Mask Composite nodes one at a time. Advanced Shape Placement takes a single shape mask and scatters a controlled number of copies across your canvas - with scale range, rotation range, overlap rules, keep-out zones, and a reproducible seed - then hands you the finished composite. It's the main event of TJ_PolkaDot (TJ16th's three-node pack) and the reason the pack exists. If you want a dense field of hearts that never quite overlap, or shapes that bleed naturally off the frame edge, this is the node you reach for.
How it works
The placement loop is straightforward and fast. It pulls every valid pixel from your region_mask (anything above 0.5), shuffles the list so you don't get a row-by-row bias, then tries again and again: pick a random position, roll a random scale between scale_min and scale_max, roll a rotation between rotation_min and rotation_max, resize and rotate the stamp, then run it through four gates - out-of-bounds ratio, overlap with already-placed shapes, coverage inside the region, and intrusion into the exclusion zone. Pass all four and it composites the shape with a max-blend and records its position, scale, rotation, width, and height. Every ten placements it filters occupied pixels out of the candidate pool (that's the "dynamic exclusion" the README brags about), and it bails out early once it hits max_consecutive_fails in a row.
Canvas size is taken from your region_mask, falling back to exclusion_mask, then base_mask, and finally a 512×512 default if you feed it none of those.
The inputs that matter
shape_mask(required) - the stamp to scatter, usually straight from ShapeMaskGenerator in the same pack.count- how many shapes to place, 1–200 (default 30). The README's "dense" recipe is 100–150.scale_min/scale_max- size range relative to the stamp (defaults 0.06–0.15).overlap_ratio- 0 to 1. 0 = shapes must not touch, 1 = full overlap allowed. This is your density knob; raise it toward 0.7–0.8 for packed collages.out_of_bounds_tolerance- how far past the canvas edge is okay. The author recommends 0.3–0.5 so shapes bleed off the edges naturally instead of all huddling inside.coverage_min,exclusion_tolerance,rotation_min/max,seed- region strictness, keep-out leniency, rotation range, and reproducibility. Defaults are sane; leave them until you need them.
The three outputs
MASK (all)- everything, including anybase_maskyou fed in.MASK (new)- only the shapes this node added. That's the chaining trick: wireMASK (new)into the next placement node'sbase_maskto layer circles on top of hearts. If you're not chaining, the two masks are identical.JSON- a STRING containing a JSON array withx,y,scale,rotation,width,heightfor every placed shape. Wire it into a Show Text node if you want to see or reuse the metadata.
Install
Standard custom-node install; deps are torch, numpy, pillow - nothing extra to pip, no models to fetch.
ComfyUI Manager → Custom Nodes Manager → search "TJ_PolkaDot" → Install → restart
# or by hand:
cd ComfyUI/custom_nodes
git clone https://github.com/TJ16th/ComfyUI-TJ-PolkaDot
# then restart ComfyUI
Note the pack targets ComfyUI's newer plugin API - if the nodes don't appear after install, update ComfyUI first.
Troubleshooting
- You placed fewer than
count. Bumpoverlap_ratio,out_of_bounds_tolerance, andmax_consecutive_fails; the node also prints its own suggestions in the console when it falls short. - The console is chatty. It prints progress unconditionally, even with
debug_modeoff. That's the pack's AI-assisted rough edge, not an error. - Shapes only fill a small box. No region/exclusion/base mask fed in means a 512×512 default canvas. Feed a
region_masksized to your image. - Placement looks clustered or biased. Change the
seed, or shrinkcount/scale_max- a tight little region can't fit 150 big shapes.
Inputs (16)
| Name | Type | Default | Description |
|---|---|---|---|
| shape_mask | MASK | Base shape mask to place multiple times | |
| count | INT | 301–200 | Number of shapes to place |
| scale_min | FLOAT | 0.0600.005–5 | Minimum scale factor |
| scale_max | FLOAT | 0.1500.005–5 | Maximum scale factor |
| rotation_min | FLOAT | -30-180–180 | Minimum rotation angle in degrees |
| rotation_max | FLOAT | 30-180–180 | Maximum rotation angle in degrees |
| overlap_ratio | FLOAT | 0.500–1 | Maximum overlap ratio with existing shapes (0=no overlap, 1=full overlap allowed) |
| coverage_min | FLOAT | 0.000–1 | Minimum coverage ratio in region mask (0.0=any overlap OK, 1.0=must be fully inside region) |
| exclusion_tolerance | FLOAT | 0.000–1 | Maximum allowed intrusion ratio into exclusion zones (0=strict prohibition, 1=full intrusion allowed) |
| out_of_bounds_tolerance | FLOAT | 0.000–1 | Maximum allowed out-of-bounds ratio (0=shapes must be fully inside canvas, 1=shapes can be completely outside) |
| max_consecutive_fails | INT | 10010–1000 | Maximum consecutive failures before early termination (10-1000) |
| seed | INT | 00–4294967295 | Random seed for reproducibility |
| debug_mode | INT | 00–1 | Enable detailed debug output (0=off, 1=on) |
| region_maskopt | MASK | Optional region mask defining where shapes can be placed (if not provided, uses full canvas) | |
| base_maskopt | MASK | Optional base mask to combine with (for chaining multiple placements) | |
| exclusion_maskopt | MASK | Optional exclusion zone mask where shapes should not be placed |
Outputs (3)
| Name | Type | Description |
|---|---|---|
| MASK (all) | MASK | Combined mask with all placed shapes (includes base_mask if provided) |
| MASK (new) | MASK | Only the newly added shapes (excludes base_mask) |
| JSON | STRING | JSON array with position, scale, rotation for each shape |