Initial commit: Image Aspect Size and Multi Switch nodes

- ImageAspectSize: reads input image dimensions and outputs width/height
  scaled to a target longest-side, snapped to multiples of 8, with a
  flip toggle for portrait/landscape rotation
- MultiSwitch: any-type switch node with dynamic slot pairs (JS-driven
  add/remove), colour-coded active/inactive sides, and clean labelling
This commit is contained in:
2026-06-01 02:18:04 +01:00
commit e41eccf1a9
16 changed files with 257 additions and 0 deletions
View File
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+43
View File
@@ -0,0 +1,43 @@
import torch
class ImageAspectSize:
TITLE = "Image Aspect Size"
CATEGORY = "JezzWTF/image"
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"image": ("IMAGE",),
"target_size": ("INT", {
"default": 1024,
"min": 64,
"max": 8192,
"step": 8,
"tooltip": "Longest side in pixels. The other dimension is calculated to preserve aspect ratio, snapped to multiples of 8.",
}),
"flip": ("BOOLEAN", {
"default": False,
"label_on": "Flipped (portrait↔landscape)",
"label_off": "Normal",
"tooltip": "Swap width and height before scaling — useful for rotating orientation without changing the image.",
}),
},
}
RETURN_TYPES = ("IMAGE", "INT", "INT")
RETURN_NAMES = ("IMAGE", "WIDTH", "HEIGHT")
FUNCTION = "calculate"
def calculate(self, image: torch.Tensor, target_size: int, flip: bool) -> tuple[torch.Tensor, int, int]:
_, H, W, _ = image.shape
if flip:
W, H = H, W
scale = target_size / max(W, H)
width = round(W * scale / 8) * 8
height = round(H * scale / 8) * 8
return (image, width, height)
+58
View File
@@ -0,0 +1,58 @@
MAX_SLOTS = 6
class AnyType(str):
"""Compares equal to every type so ComfyUI accepts any connection."""
def __ne__(self, other: object) -> bool:
return False
ANY = AnyType("*")
class ContainsAnyDict(dict):
"""An empty dict that reports containing any key.
Empty → ComfyUI renders no input slots from the Python definition.
__contains__ always True → any JS-added input name passes validation.
dict subclass → JSON serialisable (serialises as {})."""
def __contains__(self, key: object) -> bool:
return True
def __getitem__(self, key: str) -> tuple:
return (ANY, {})
class MultiSwitch:
TITLE = "Multi Switch"
CATEGORY = "JezzWTF/utils"
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"switch": ("BOOLEAN", {
"default": False,
"label_on": "true",
"label_off": "false",
"tooltip": "Option A → false_N inputs pass through. Option B → true_N inputs pass through.",
}),
},
"optional": ContainsAnyDict(),
"hidden": {
"num_slots": ("INT", {"default": 2}),
},
}
@classmethod
def VALIDATE_INPUTS(cls, input_types):
return True
RETURN_TYPES = (ANY,) * MAX_SLOTS
RETURN_NAMES = tuple(f"output_{i}" for i in range(1, MAX_SLOTS + 1))
FUNCTION = "execute"
def execute(self, switch: bool, num_slots: int = 2, **kwargs) -> tuple:
side = "true" if switch else "false"
return tuple(kwargs.get(f"{side}_{i}", None) for i in range(1, MAX_SLOTS + 1))