Nodes/TJ_PolkaDot/Advanced Shape Placement
ComfyUI Node

Advanced Shape Placement

150 non-overlapping shapes in seconds — the collage node that does the fiddly work

By TJ16th·Created 11 months ago·Updated 11 months ago· 2
Advanced Shape Placement
  • shape_mask
  • region_mask
  • base_mask
  • exclusion_mask
  • MASK (all)
  • MASK (new)
  • JSON
count30
scale_min0.060
scale_max0.150
rotation_min-30
rotation_max30
overlap_ratio0.50
coverage_min0.00
exclusion_tolerance0.00
out_of_bounds_tolerance0.00
max_consecutive_fails100
seed0
debug_mode0

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 any base_mask you fed in.
  • MASK (new) - only the shapes this node added. That's the chaining trick: wire MASK (new) into the next placement node's base_mask to layer circles on top of hearts. If you're not chaining, the two masks are identical.
  • JSON - a STRING containing a JSON array with x, y, scale, rotation, width, height for 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. Bump overlap_ratio, out_of_bounds_tolerance, and max_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_mode off. 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_mask sized to your image.
  • Placement looks clustered or biased. Change the seed, or shrink count/scale_max - a tight little region can't fit 150 big shapes.
CategoryTJ_PolkaDot

Inputs (16)

NameTypeDefaultDescription
shape_maskMASKBase shape mask to place multiple times
countINT301–200Number of shapes to place
scale_minFLOAT0.0600.005–5Minimum scale factor
scale_maxFLOAT0.1500.005–5Maximum scale factor
rotation_minFLOAT-30-180–180Minimum rotation angle in degrees
rotation_maxFLOAT30-180–180Maximum rotation angle in degrees
overlap_ratioFLOAT0.500–1Maximum overlap ratio with existing shapes (0=no overlap, 1=full overlap allowed)
coverage_minFLOAT0.000–1Minimum coverage ratio in region mask (0.0=any overlap OK, 1.0=must be fully inside region)
exclusion_toleranceFLOAT0.000–1Maximum allowed intrusion ratio into exclusion zones (0=strict prohibition, 1=full intrusion allowed)
out_of_bounds_toleranceFLOAT0.000–1Maximum allowed out-of-bounds ratio (0=shapes must be fully inside canvas, 1=shapes can be completely outside)
max_consecutive_failsINT10010–1000Maximum consecutive failures before early termination (10-1000)
seedINT00–4294967295Random seed for reproducibility
debug_modeINT00–1Enable detailed debug output (0=off, 1=on)
region_maskoptMASKOptional region mask defining where shapes can be placed (if not provided, uses full canvas)
base_maskoptMASKOptional base mask to combine with (for chaining multiple placements)
exclusion_maskoptMASKOptional exclusion zone mask where shapes should not be placed

Outputs (3)

NameTypeDescription
MASK (all)MASKCombined mask with all placed shapes (includes base_mask if provided)
MASK (new)MASKOnly the newly added shapes (excludes base_mask)
JSONSTRINGJSON array with position, scale, rotation for each shape