611 lines
22 KiB
Python
611 lines
22 KiB
Python
# ==============================================================================
|
|
# This file contains code that has been adapted or directly copied from the
|
|
# ComfyUI-Impact-Pack package by Dr.Lt.Data ("ltdrdata").
|
|
# Original source: https://github.com/ltdrdata/ComfyUI-Impact-Pack
|
|
#
|
|
# This code is used under the terms of the original license, with modifications
|
|
# made to suit the needs of this project.
|
|
# ==============================================================================
|
|
import torch
|
|
import torch.nn.functional as F
|
|
|
|
import time, hashlib
|
|
import base64, io
|
|
from PIL import Image
|
|
|
|
|
|
|
|
|
|
class PseudoMaskBlur:
|
|
"""
|
|
Utility class for applying a Gaussian blur to image masks.
|
|
Inputs:
|
|
msk (tensor): The input image tensor. Expected shape is [1, H, W]
|
|
blur_radius (int): The radius of the Gaussian blur kernel (default: 1, min: 1, max: 31, step: 1).
|
|
sigma (float): The standard deviation of the Gaussian kernel (default: 1.0, min: 0.1, max: 10.0, step: 0.1).
|
|
Outputs:
|
|
msk (tensor): The blurred image tensor, with the same shape as the input.
|
|
"""
|
|
@classmethod
|
|
def INPUT_TYPES(s):
|
|
return {
|
|
"required": {
|
|
"msks_list": ("MASK",),
|
|
"blur_radius": ("INT", {
|
|
"default": 25,
|
|
"min": 1,
|
|
"max": 51,
|
|
"step": 1
|
|
}),
|
|
"sigma": ("FLOAT", {
|
|
"default": 1.0,
|
|
"min": 0.1,
|
|
"max": 20.0,
|
|
"step": 0.1
|
|
}),
|
|
"invert": ("BOOLEAN", {"default": False}),
|
|
},
|
|
}
|
|
|
|
INPUT_IS_LIST = True
|
|
OUTPUT_IS_LIST = (True,)
|
|
RETURN_TYPES = ("MASK",)
|
|
RETURN_NAMES = ("msks",)
|
|
FUNCTION = "blur"
|
|
CATEGORY = "Pseudocomfy/Utils"
|
|
|
|
def blur(self, msks_list, blur_radius: int, sigma: float, invert):
|
|
"""
|
|
Apply Gaussian blur to a list of masks.
|
|
Expects each mask to be (1, H, W) or (B, H, W)
|
|
"""
|
|
# given that INPUT_IS_LIST, msks_list is a list of masks
|
|
# if inputs that are meant to be singletons are a list, use the first element
|
|
if isinstance(blur_radius, list) and len(blur_radius)>0: blur_radius = blur_radius[0]
|
|
if isinstance(sigma, list) and len(sigma)>0: sigma = sigma[0]
|
|
if isinstance(invert, list) and len(invert)>0: invert = invert[0]
|
|
|
|
|
|
print(f"[pseudocomfy] BlurMask blur_radius:{blur_radius} sigma:{sigma}")
|
|
|
|
results = []
|
|
for msk in msks_list:
|
|
print(f"\tmsk shape:{tuple(msk.shape)}")
|
|
|
|
if blur_radius == 0:
|
|
results.append(msk)
|
|
continue
|
|
|
|
device = msk.device
|
|
|
|
# Ensure batch dimension
|
|
if msk.ndim == 2:
|
|
msk = msk.unsqueeze(0) # (1, H, W)
|
|
|
|
B, H, W = msk.shape
|
|
|
|
# Add channel dimension for conv2d
|
|
msk = msk.unsqueeze(1) # (B, 1, H, W)
|
|
|
|
kernel_size = blur_radius * 2 + 1
|
|
kernel = self.gaussian_kernel(kernel_size, sigma, device=device)
|
|
kernel = kernel.expand(1, 1, kernel_size, kernel_size)
|
|
|
|
pad = blur_radius
|
|
padded_image = F.pad(msk, (pad, pad, pad, pad), mode='reflect')
|
|
blurred = F.conv2d(padded_image, kernel, padding=0, groups=1)
|
|
blurred = blurred[:, :, pad:-pad, pad:-pad] # Remove extra padding
|
|
|
|
# Remove channel dimension
|
|
result = blurred.squeeze(1) # (B, H, W)
|
|
|
|
if invert:
|
|
result = 1.0 - result
|
|
result = torch.clamp(result, 0.0, 1.0)
|
|
|
|
results.append(result)
|
|
return (results,)
|
|
|
|
def gaussian_kernel(self, kernel_size, sigma, device):
|
|
"""Create a 2D Gaussian kernel."""
|
|
coords = torch.arange(kernel_size, dtype=torch.float32, device=device) - (kernel_size - 1) / 2
|
|
grid = coords.unsqueeze(0) ** 2 + coords.unsqueeze(1) ** 2
|
|
kernel = torch.exp(-0.5 * grid / sigma ** 2)
|
|
kernel = kernel / kernel.sum()
|
|
return kernel
|
|
|
|
class PseudoMaskClamp:
|
|
"""
|
|
Utility class for clamping mask values to a specified range.
|
|
Inputs:
|
|
msk (tensor): The input mask tensor. Expected shape is [1, H, W] or [B, H, W], values in [0, 1].
|
|
min_val (float): Minimum value to clamp to (default: 0.0, min: 0.0, max: 1.0, step: 0.01).
|
|
max_val (float): Maximum value to clamp to (default: 1.0, min: 0.0, max: 1.0, step: 0.01).
|
|
Outputs:
|
|
msk (tensor): The clamped mask tensor, same shape as input.
|
|
"""
|
|
@classmethod
|
|
def INPUT_TYPES(s):
|
|
return {
|
|
"required": {
|
|
"msks_list": ("MASK",),
|
|
"min_val": ("FLOAT", {
|
|
"default": 0.0,
|
|
"min": 0.0,
|
|
"max": 1.0,
|
|
"step": 0.01
|
|
}),
|
|
"max_val": ("FLOAT", {
|
|
"default": 1.0,
|
|
"min": 0.0,
|
|
"max": 1.0,
|
|
"step": 0.01
|
|
}),
|
|
},
|
|
"optional": {
|
|
}
|
|
}
|
|
|
|
INPUT_IS_LIST = True
|
|
OUTPUT_IS_LIST = (True,)
|
|
RETURN_TYPES = ("MASK",)
|
|
RETURN_NAMES = ("msks",)
|
|
FUNCTION = "clamp"
|
|
CATEGORY = "Pseudocomfy/Utils"
|
|
|
|
def clamp(self, msks_list, min_val: float, max_val: float):
|
|
"""
|
|
Clamp mask values to [min_val, max_val] for a list of masks.
|
|
"""
|
|
# given that INPUT_IS_LIST, msks_list is a list of masks
|
|
# if inputs that are meant to be singletons are a list, use the first element
|
|
if isinstance(min_val, list) and len(min_val)>0: min_val = min_val[0]
|
|
if isinstance(max_val, list) and len(max_val)>0: max_val = max_val[0]
|
|
|
|
print(f"[pseudocomfy] ClampMask")
|
|
|
|
results = []
|
|
for msk in msks_list:
|
|
print(f"\tmsk shape:{tuple(msk.shape)}")
|
|
print(f"\tclamping mask of ({msk.min():.3f} -> {msk.max():.3f}) to ({min_val:.3f} -> {max_val:.3f})")
|
|
clamped = torch.clamp(msk, min=min_val, max=max_val)
|
|
results.append(clamped)
|
|
return (results,)
|
|
|
|
class PseudoMaskRemap:
|
|
"""
|
|
Utility class for remapping mask values from a source range to a target range.
|
|
Inputs:
|
|
msk (tensor): The input mask tensor. Expected shape is [1, H, W] or [B, H, W], values in [0, 1].
|
|
src_min (float, optional): Source minimum value to remap from. If not set, uses mask min.
|
|
src_max (float, optional): Source maximum value to remap from. If not set, uses mask max.
|
|
tgt_min (float, optional): Target minimum value to remap to. If not set, uses 0.0.
|
|
tgt_max (float, optional): Target maximum value to remap to. If not set, uses 1.0.
|
|
Outputs:
|
|
msk (tensor): The remapped mask tensor, same shape as input.
|
|
"""
|
|
@classmethod
|
|
def INPUT_TYPES(s):
|
|
return {
|
|
"required": {
|
|
"msks_list": ("MASK",),
|
|
},
|
|
"optional": {
|
|
"src_min": ("FLOAT", {"forceInput": True}), # force input b/c can't have widget for None
|
|
"src_max": ("FLOAT", {"forceInput": True}), # force input b/c can't have widget for None
|
|
"tgt_min": ("FLOAT", {
|
|
"default": 0.0,
|
|
"min": 0.0,
|
|
"max": 1.0,
|
|
"step": 0.01
|
|
}),
|
|
"tgt_max": ("FLOAT", {
|
|
"default": 1.0,
|
|
"min": 0.0,
|
|
"max": 1.0,
|
|
"step": 0.01
|
|
}),
|
|
}
|
|
}
|
|
|
|
INPUT_IS_LIST = True
|
|
OUTPUT_IS_LIST = (True,)
|
|
RETURN_TYPES = ("MASK",)
|
|
RETURN_NAMES = ("msks",)
|
|
FUNCTION = "remap"
|
|
CATEGORY = "Pseudocomfy/Utils"
|
|
|
|
def remap(self, msks_list, src_min: float = None, src_max: float = None, tgt_min: float = 0.0, tgt_max: float = 1.0):
|
|
"""
|
|
Remap mask values from [src_min, src_max] to [tgt_min, tgt_max] for a list of masks.
|
|
"""
|
|
# given that INPUT_IS_LIST, msks_list is a list of masks
|
|
# if inputs that are meant to be singletons are a list, use the first element
|
|
if isinstance(src_min, list) and len(src_min)>0: src_min = src_min[0]
|
|
if isinstance(src_max, list) and len(src_max)>0: src_max = src_max[0]
|
|
if isinstance(tgt_min, list) and len(tgt_min)>0: tgt_min = tgt_min[0]
|
|
if isinstance(tgt_max, list) and len(tgt_max)>0: tgt_max = tgt_max[0]
|
|
|
|
print(f"[pseudocomfy] RemapMask")
|
|
|
|
results = []
|
|
for msk in msks_list:
|
|
mask_min = float(msk.min())
|
|
mask_max = float(msk.max())
|
|
from_min = src_min if src_min is not None else mask_min
|
|
from_max = src_max if src_max is not None else mask_max
|
|
to_min = tgt_min
|
|
to_max = tgt_max
|
|
|
|
print(f"\tmsk shape:{tuple(msk.shape)}")
|
|
print(f"\tremapping mask of ({mask_min:.3f} -> {mask_max:.3f}) from ({from_min:.3f} -> {from_max:.3f}) to ({to_min:.3f} -> {to_max:.3f})")
|
|
|
|
# Avoid division by zero
|
|
if from_max - from_min == 0:
|
|
remapped = torch.full_like(msk, to_min)
|
|
else:
|
|
norm = (msk - from_min) / (from_max - from_min)
|
|
remapped = norm * (to_max - to_min) + to_min
|
|
remapped = torch.clamp(remapped, min(min(to_min, to_max), 0.0), max(max(to_min, to_max), 1.0))
|
|
results.append(remapped)
|
|
return (results,)
|
|
|
|
class PseudoMaskInvert:
|
|
"""
|
|
Utility class for inverting mask values (1 - mask).
|
|
Inputs:
|
|
msk (tensor): The input mask tensor. Expected shape is [1, H, W] or [B, H, W], values in [0, 1].
|
|
Outputs:
|
|
msk (tensor): The inverted mask tensor, same shape as input.
|
|
"""
|
|
@classmethod
|
|
def INPUT_TYPES(s):
|
|
return {
|
|
"required": {
|
|
"msks_list": ("MASK",),
|
|
}
|
|
}
|
|
|
|
INPUT_IS_LIST = True
|
|
OUTPUT_IS_LIST = (True,)
|
|
RETURN_TYPES = ("MASK",)
|
|
RETURN_NAMES = ("msks",)
|
|
FUNCTION = "invert"
|
|
CATEGORY = "Pseudocomfy/Utils"
|
|
|
|
def invert(self, msks_list):
|
|
"""
|
|
Invert mask values (1 - mask) for a list of masks.
|
|
"""
|
|
# given that INPUT_IS_LIST, msks_list is a list of masks
|
|
|
|
print(f"[pseudocomfy] MaskInvert")
|
|
|
|
results = []
|
|
for msk in msks_list:
|
|
mask_min = float(msk.min())
|
|
mask_max = float(msk.max())
|
|
print(f"\tmsk shape:{tuple(msk.shape)}")
|
|
print(f"\tinverting mask of ({mask_min:.3f} -> {mask_max:.3f}) to ({1-mask_max:.3f} -> {1-mask_min:.3f})")
|
|
inverted = 1.0 - msk
|
|
inverted = torch.clamp(inverted, 0.0, 1.0)
|
|
results.append(inverted)
|
|
return (results,)
|
|
|
|
class PseudoMaskReshape:
|
|
"""
|
|
Utility class for morphological operations on masks: Erode (shrink) and Dilate (expand) white regions.
|
|
Inputs:
|
|
msk (tensor): The input mask tensor. Expected shape is [1, H, W] or [B, H, W], values in [0, 1].
|
|
operation (str): "erode" or "dilate".
|
|
kernel_size (int): Size of the square structuring element (default: 3, min: 1, max: 31, step: 2).
|
|
iterations (int): Number of times to apply the operation (default: 1, min: 1, max: 10, step: 1).
|
|
Outputs:
|
|
msk (tensor): The processed mask tensor, same shape as input.
|
|
"""
|
|
@classmethod
|
|
def INPUT_TYPES(s):
|
|
return {
|
|
"required": {
|
|
"msks_list": ("MASK",),
|
|
"operation": (["erode (shrink white areas)", "dilate (grow white areas)"], {"default": "dilate (grow white areas)"}),
|
|
"kernel_size": ("INT", {
|
|
"default": 3,
|
|
"min": 1,
|
|
"max": 31,
|
|
"step": 2
|
|
}),
|
|
"iterations": ("INT", {
|
|
"default": 1,
|
|
"min": 1,
|
|
"max": 10,
|
|
"step": 1
|
|
}),
|
|
"invert": ("BOOLEAN", {"default": False}),
|
|
}
|
|
}
|
|
|
|
INPUT_IS_LIST = True
|
|
OUTPUT_IS_LIST = (True,)
|
|
RETURN_TYPES = ("MASK",)
|
|
RETURN_NAMES = ("msks",)
|
|
FUNCTION = "morph"
|
|
CATEGORY = "Pseudocomfy/Utils"
|
|
|
|
def morph(self, msks_list, operation: str, kernel_size: int, iterations: int, invert):
|
|
"""
|
|
Apply morphological operation to a list of masks.
|
|
"""
|
|
# given that INPUT_IS_LIST, msks_list is a list of masks
|
|
# if inputs that are meant to be singletons are a list, use the first element
|
|
if isinstance(operation, list) and len(operation)>0: operation = operation[0]
|
|
if isinstance(kernel_size, list) and len(kernel_size)>0: kernel_size = kernel_size[0]
|
|
if isinstance(iterations, list) and len(iterations)>0: iterations = iterations[0]
|
|
if isinstance(invert, list) and len(invert)>0: invert = invert[0]
|
|
|
|
|
|
print(f"[pseudocomfy] MaskMorphology")
|
|
print(f"\toperation: {operation}, kernel_size: {kernel_size}, iterations: {iterations}, invert: {invert}")
|
|
|
|
results = []
|
|
for msk in msks_list:
|
|
print(f"\tmsk shape:{tuple(msk.shape)}")
|
|
|
|
# Ensure batch dimension
|
|
if msk.ndim == 2:
|
|
msk = msk.unsqueeze(0) # (1, H, W)
|
|
B, H, W = msk.shape
|
|
|
|
# Add channel dimension for conv2d
|
|
msk = msk.unsqueeze(1) # (B, 1, H, W)
|
|
|
|
# Create structuring element (kernel)
|
|
kernel = torch.ones((1, 1, kernel_size, kernel_size), dtype=msk.dtype, device=msk.device)
|
|
|
|
result = msk
|
|
for _ in range(iterations):
|
|
if operation[:3] == "ero":
|
|
result = torch.nn.functional.max_pool2d(
|
|
1.0 - result, kernel_size=kernel_size, stride=1, padding=kernel_size // 2
|
|
)
|
|
result = 1.0 - result
|
|
elif operation[:3] == "dil":
|
|
result = torch.nn.functional.max_pool2d(
|
|
result, kernel_size=kernel_size, stride=1, padding=kernel_size // 2
|
|
)
|
|
else:
|
|
raise ValueError(f"Unknown operation: {operation}")
|
|
|
|
# Remove channel dimension
|
|
result = result.squeeze(1) # (B, H, W)
|
|
|
|
if invert:
|
|
result = 1.0 - result
|
|
result = torch.clamp(result, 0.0, 1.0)
|
|
|
|
print(f"\tresult range: ({result.min():.3f} -> {result.max():.3f})")
|
|
results.append(result)
|
|
return (results,)
|
|
|
|
|
|
class PseudoMaskAggregate:
|
|
"""
|
|
Utility class for combining a list of masks into a single mask using various arithmetic operations.
|
|
Inputs:
|
|
msks_list (list[tensor]): List of mask tensors, each [1, H, W] or [B, H, W], values in [0, 1].
|
|
operation (str): Operation to perform. One of:
|
|
- "sum_clamped": Sum all masks, clamp to [0, 1].
|
|
- "sum_normalized": Sum all masks, then normalize result to [0, 1].
|
|
- "average": Pixelwise mean of all masks.
|
|
- "max": Pixelwise max of all masks.
|
|
- "min": Pixelwise min of all masks.
|
|
invert (bool): If True, invert the result mask (1 - mask).
|
|
Outputs:
|
|
msk (tensor): The combined mask tensor, same shape as input.
|
|
"""
|
|
@classmethod
|
|
def INPUT_TYPES(s):
|
|
return {
|
|
"required": {
|
|
"msks_list": ("MASK", {"forceInput": True, "isList": True}),
|
|
"operation": (["sum_clamped", "sum_normalized", "average", "max", "min"], {}),
|
|
"invert": ("BOOLEAN", {"default": False}),
|
|
}
|
|
}
|
|
|
|
INPUT_IS_LIST = True
|
|
RETURN_TYPES = ("MASK",)
|
|
RETURN_NAMES = ("msk",)
|
|
FUNCTION = "combine"
|
|
CATEGORY = "Pseudocomfy/Utils"
|
|
|
|
def combine(self, msks_list, operation, invert):
|
|
# given that INPUT_IS_LIST, msks_list is a list of masks
|
|
# if inputs that are meant to be singletons are a list, use the first element
|
|
if isinstance(operation, list) and len(operation)>0: operation = operation[0]
|
|
if isinstance(invert, list) and len(invert)>0: invert = invert[0]
|
|
|
|
# Stack masks to shape (N, H, W) or (N, 1, H, W)
|
|
msks = [m.float() for m in msks_list]
|
|
stack = torch.stack(msks, dim=0)
|
|
print(f"[pseudocomfy] CombineMasks")
|
|
print(f"\toperation: {operation}")
|
|
print(f"\tinput count: {len(msks)}")
|
|
print(f"\tstack shape: {tuple(stack.shape)}")
|
|
|
|
if operation == "sum_clamped":
|
|
result = stack.sum(dim=0)
|
|
result = torch.clamp(result, 0.0, 1.0)
|
|
elif operation == "sum_normalized":
|
|
result = stack.sum(dim=0)
|
|
minv, maxv = result.min(), result.max()
|
|
if maxv - minv == 0:
|
|
result = torch.zeros_like(result)
|
|
else:
|
|
result = (result - minv) / (maxv - minv)
|
|
elif operation == "average":
|
|
result = stack.mean(dim=0)
|
|
elif operation == "max":
|
|
result = stack.max(dim=0).values
|
|
elif operation == "min":
|
|
result = stack.min(dim=0).values
|
|
else:
|
|
raise ValueError(f"Unknown operation: {operation}")
|
|
|
|
if invert:
|
|
result = 1.0 - result
|
|
result = torch.clamp(result, 0.0, 1.0)
|
|
|
|
print(f"\tresult shape: {tuple(result.shape)}")
|
|
print(f"\tresult range: ({result.min():.3f} -> {result.max():.3f})")
|
|
return (result,)
|
|
|
|
|
|
|
|
class PseudoPreviewStrings:
|
|
"""
|
|
Utility class for previewing a single string in the ComfyUI UI.
|
|
Inputs:
|
|
string (str): The string(s) to preview.
|
|
Outputs:
|
|
none
|
|
"""
|
|
@classmethod
|
|
def INPUT_TYPES(s):
|
|
return {
|
|
"required": {
|
|
"strings": ("STRING", {"forceInput": True}),
|
|
},
|
|
}
|
|
INPUT_IS_LIST = True
|
|
RETURN_TYPES = ()
|
|
RETURN_NAMES = ()
|
|
FUNCTION = "notify"
|
|
OUTPUT_NODE = True
|
|
CATEGORY = "Pseudocomfy/Utils"
|
|
|
|
def notify(self, strings):
|
|
# Optionally handle extra_pnginfo/unique_id if needed
|
|
return {
|
|
"ui": {"strings": strings}
|
|
}
|
|
|
|
|
|
class PseudoConcatStrings:
|
|
"""
|
|
Utility class for concatenating two strings with a selectable separator.
|
|
Inputs:
|
|
str_a (str): The first string.
|
|
str_b (str): The second string.
|
|
separator (str): The separator to use ("space", "comma", or "semicolon").
|
|
Outputs:
|
|
result (str): The concatenated string.
|
|
"""
|
|
@classmethod
|
|
def INPUT_TYPES(s):
|
|
return {
|
|
"required": {
|
|
"str_a": ("STRING", {"forceInput": True}),
|
|
"str_b": ("STRING", {"forceInput": True}),
|
|
"separator": (["space", "comma", "semicolon"],{}),
|
|
},
|
|
}
|
|
|
|
RETURN_TYPES = ("STRING",)
|
|
RETURN_NAMES = ("str",)
|
|
FUNCTION = "concat"
|
|
CATEGORY = "Pseudocomfy/Utils"
|
|
|
|
def concat(self, str_a, str_b, separator):
|
|
sep_map = {
|
|
"space": " ",
|
|
"comma": ", ",
|
|
"semicolon": "; "
|
|
}
|
|
sep = sep_map.get(separator, " ")
|
|
result = f"{str_a}{sep}{str_b}"
|
|
return (result,)
|
|
|
|
|
|
class PseudoRemapNormalizedFloat:
|
|
"""
|
|
Utility class for remapping a float value from a source range to a target range.
|
|
Inputs:
|
|
value (float): The input float value.
|
|
src_min (float): Source minimum value (default: 0.0).
|
|
src_max (float): Source maximum value (default: 1.0).
|
|
tgt_min (float): Target minimum value (default: 0.0).
|
|
tgt_max (float): Target maximum value (default: 1.0).
|
|
Outputs:
|
|
value (float): The remapped float value.
|
|
"""
|
|
@classmethod
|
|
def INPUT_TYPES(s):
|
|
return {
|
|
"required": {
|
|
"val": ("FLOAT", {"forceInput": True}),
|
|
"tgt_min": ("FLOAT", {"default": 0.0}),
|
|
"tgt_max": ("FLOAT", {"default": 1.0}),
|
|
},
|
|
}
|
|
|
|
RETURN_TYPES = ("FLOAT",)
|
|
RETURN_NAMES = ("val",)
|
|
FUNCTION = "remap"
|
|
CATEGORY = "Pseudocomfy/Utils"
|
|
|
|
def remap(self, val, tgt_min, tgt_max):
|
|
print(f"[pseudocomfy] RemapFloat")
|
|
print(f"\tvalue: {val}")
|
|
print(f"\t0-1 ==> {tgt_min}-{tgt_max}")
|
|
|
|
src_min, src_max = 0.0, 1.0 # Default source range
|
|
|
|
# Ensure min <= max
|
|
if tgt_min > tgt_max:
|
|
tgt_min, tgt_max = tgt_max, tgt_min
|
|
|
|
# Clamp value to source domain
|
|
val = max(min(val, src_max), src_min)
|
|
|
|
if src_max - src_min == 0:
|
|
remapped = tgt_min
|
|
else:
|
|
norm = (val - src_min) / (src_max - src_min)
|
|
remapped = norm * (tgt_max - tgt_min) + tgt_min
|
|
return (remapped,)
|
|
|
|
|
|
class PseudoFloatToInt:
|
|
"""
|
|
Utility class for converting a float value to an integer.
|
|
Inputs:
|
|
val (float): The input float value.
|
|
rounding (str): Rounding method: "round" (default), "floor", or "ceil".
|
|
Outputs:
|
|
value (int): The converted integer value.
|
|
"""
|
|
@classmethod
|
|
def INPUT_TYPES(s):
|
|
return {
|
|
"required": {
|
|
"val": ("FLOAT", {"forceInput": True}),
|
|
"rounding": (["round", "floor", "ceil"], {"default": "round"}),
|
|
},
|
|
}
|
|
|
|
RETURN_TYPES = ("INT",)
|
|
RETURN_NAMES = ("int",)
|
|
FUNCTION = "convert"
|
|
CATEGORY = "Pseudocomfy/Utils"
|
|
|
|
def convert(self, val, rounding="round"):
|
|
import math
|
|
if rounding == "floor":
|
|
result = int(math.floor(val))
|
|
elif rounding == "ceil":
|
|
result = int(math.ceil(val))
|
|
else:
|
|
result = int(round(val))
|
|
print(f"[pseudocomfy] FloatToInt: {val} -> {result} (method: {rounding})")
|
|
return (result,) |