"Rotate" is the word people actually search for, and almost nothing in
the ecosystem registers under that name, so keeping it in the display
name is worth more than a tidier one. The node menu now shows
"Transform & Rotate Pro", and the class is TransformRotatePro to match.
The registration key stays MP_ImageTransform on purpose. That key is
what saved workflows and placed nodes reference, so changing it again
would break any node already dropped on a canvas. The key is never
shown in the UI, so there is no cost to leaving it as it is - a comment
in __init__.py records why it differs from the class name.
Category is unchanged at MachinePaintingNodes/Rotate & Scale. README
updated throughout.
The node does rotate, scale, move, canvas resize, mask cut-out and
background fill, so "rotate" undersold it. "Transform" is the standard
term for rotate + scale + translate, matches the existing
MachinePaintingNodes/Transform category, matches the pack's "... Pro"
convention, and leaves room for flip / skew / corner-pin later.
image_rotate.py -> image_transform.py
class ImageRotate -> ImageTransformPro
MP_ImageRotate -> MP_ImageTransform
display name -> Image Transform Pro
Also adds a DESCRIPTION attribute. ComfyUI reads DESCRIPTION rather
than the docstring for the node tooltip and indexes it for node search,
so the node is still found by typing "rotate", "scale" or "move" even
though those words are no longer in its name.
Free to rename: the node has never shipped in a release, so no saved
workflow refers to the old key.
ImageRotate is a generic internal name that other node packs also
register. ComfyUI merges every pack's NODE_CLASS_MAPPINGS into a single
dict, so identical keys silently overwrite each other and whichever pack
loads last wins - leaving the node missing from the menu.
Renamed the registration key to MP_ImageRotate in both NODE_CLASS_MAPPINGS
and NODE_DISPLAY_NAME_MAPPINGS, matching the existing MP_ImageMaskSwitch
precedent and rule 5 in CLAUDE.md. The Python class name and the
"Image Rotate" display name are unchanged. Safe to rename now because
the node has not shipped in a release, so no saved workflow refers to
the old key.
Image Rotate (new node): rotate, zoom and reposition an image inside a
definable canvas. Free or fixed (45 degree snap) rotation, centre-zero
angle slider (CCW left, CW right), exponential centre-zero zoom
(-100 = 0.25x, +100 = 4x), percent-based XY placement, and
transparent/black/white background fill. An optional mask input rides
the identical matrix so it stays registered to the image. Canvas W/H
inputs default to 0, meaning "match the incoming image".
Remove Background Pro: rotation block added at the bottom of the
settings (enable toggle, free/fixed method, angle slider, XY
placement). Applied after removal so rembg always segments the upright
image; the canvas stays at the original size and image, mask,
masked_image, mask_bw and passthrough are all warped by the same
matrix.
New transform_utils module keeps the rotate/zoom/offset math identical
across both nodes. js/image_transform.js snaps the angle slider to 45
degree steps in fixed mode and hides the rotation widgets until the
toggle is on; Python snaps too, so the result is correct regardless of
frontend version.
New Nodes:
- Mask Composite Pro 2X: combine 2 masks with per-mask blur/grow-shrink/opacity, screen blend, output selector, invert
- Mask Composite Pro 6X: combine up to 6 masks with same features
Updates:
- Inpaint Mask Pro: added expand/shrink, removed SEGS (simplified to image + mask), inpaint_mask now required, multi-color overlay
- Blur Pro: type-specific sliders marked as advanced for cleaner default view
- Widget visibility uses Python "advanced" flag for new ComfyUI frontend compatibility
- Added CLAUDE.md with versioning and registration rules
- Bump to v2.1.1, 40 nodes total
The requirements.txt was missing opencv-python-headless, which is
imported by 7 node files at the top level. When cv2 is not installed,
the entire module fails to load silently, causing KeyError for all
nodes including SeedLock.
Replace fragile top-level `from nodes import EmptyLatentImage` with
a direct torch implementation, fixing plugin load failure. Also update
version string and add _old/ to .gitignore.