launch
0.0.1
This commit is contained in:
@@ -1,2 +1,72 @@
|
||||
# ComfyUI-Utilitools
|
||||
ComfyUI Custom Nodes: Utilities and Tools for improving user experience.
|
||||
|
||||
A collection of utility nodes for ComfyUI that I find handy. Nothing fancy, just stuff that makes workflows a bit easier when you need simple operations.
|
||||
|
||||
## What's in here?
|
||||
|
||||
These aren't the most sophisticated nodes out there, but they get the job done for basic operations:
|
||||
|
||||
### 🧮 Math Nodes
|
||||
- **Add, Subtract, Multiply, Divide** - Basic math with both int and float outputs
|
||||
- **Calculator** - Safely evaluate simple math expressions like "2 + 2 * 3"
|
||||
|
||||
### 📝 Text Utilities
|
||||
- **Text Concatenation** - Join two strings with optional separator
|
||||
- **String Replace** - Find and replace text with optional count limit
|
||||
|
||||
### 🔄 Type Conversions
|
||||
- **Float to Int / Int to Float** - Convert between number types
|
||||
- **Whatever To String** - Convert any value (int, float, string) to string
|
||||
|
||||
### 🤔 Logic & Control
|
||||
- **If Then Else** - Branch workflows based on boolean conditions
|
||||
- **Switch** - Select from up to 8 inputs by index
|
||||
- **Boolean Gates** - AND, OR, NOT operations
|
||||
|
||||
### 📋 Data Manipulation
|
||||
- **Create List** - Make lists from up to 8 items
|
||||
- **List Index** - Get item from list by index
|
||||
- **Batch Controller** - Adjust tensor batch sizes
|
||||
|
||||
### ⚙️ Workflow Helpers
|
||||
- **Passthrough** - For workflow organization (does nothing, just passes data through)
|
||||
- **Counter** - Increment/decrement with optional reset
|
||||
- **Constants** - Int, Float, and String constant value nodes
|
||||
|
||||
### 🖼️ Image Utilities
|
||||
- **Image Dimensions** - Get width, height, and batch size from images
|
||||
- **Aspect Ratio** - Calculate ratio as decimal and simplified fraction (like "16:9")
|
||||
|
||||
## Installation
|
||||
|
||||
Just drop this folder into your ComfyUI `custom_nodes` directory and restart ComfyUI. That's it.
|
||||
|
||||
```
|
||||
ComfyUI/custom_nodes/ComfyUI-Utilitools/
|
||||
```
|
||||
|
||||
## Notes
|
||||
|
||||
- All nodes are organized under "Utilitools" categories in the node browser
|
||||
- Most math operations work with both int and float inputs
|
||||
- The calculator node only accepts basic math expressions for safety
|
||||
- These are utility nodes I made for myself - they're useful but not groundbreaking
|
||||
|
||||
## Structure
|
||||
|
||||
The code is organized by category for easier maintenance:
|
||||
|
||||
```
|
||||
nodes/
|
||||
├── math.py # Arithmetic operations
|
||||
├── text.py # Text processing
|
||||
├── conversion.py # Type conversions
|
||||
├── logic.py # Conditional logic
|
||||
├── data.py # Lists and batching
|
||||
├── workflow.py # Workflow helpers
|
||||
├── constants.py # Constant values
|
||||
├── image.py # Image utilities
|
||||
└── main.py # Central registry
|
||||
```
|
||||
|
||||
Feel free to use, modify, or ignore as needed!
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
from .nodes.main import NODE_CLASS_MAPPINGS, NODE_DISPLAY_NAME_MAPPINGS
|
||||
|
||||
__all__ = ['NODE_CLASS_MAPPINGS', 'NODE_DISPLAY_NAME_MAPPINGS']
|
||||
@@ -0,0 +1,12 @@
|
||||
"""
|
||||
ComfyUI Utilitools Nodes
|
||||
|
||||
A collection of utility nodes for ComfyUI to improve workflow efficiency.
|
||||
"""
|
||||
|
||||
from .main import NODE_CLASS_MAPPINGS, NODE_DISPLAY_NAME_MAPPINGS
|
||||
|
||||
__version__ = "1.0.0"
|
||||
__author__ = "ComfyUI-Utilitools"
|
||||
|
||||
__all__ = ["NODE_CLASS_MAPPINGS", "NODE_DISPLAY_NAME_MAPPINGS"]
|
||||
@@ -0,0 +1,65 @@
|
||||
class UtilConstantInt:
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"value": ("INT", {"default": 0, "min": -999999, "max": 999999, "step": 1}),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("INT",)
|
||||
RETURN_NAMES = ("value",)
|
||||
FUNCTION = "get_value"
|
||||
CATEGORY = "Utilitools/Constants"
|
||||
|
||||
def get_value(self, value):
|
||||
return (value,)
|
||||
|
||||
|
||||
class UtilConstantFloat:
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"value": ("FLOAT", {"default": 0.0, "min": -999999.0, "max": 999999.0, "step": 0.001}),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("FLOAT",)
|
||||
RETURN_NAMES = ("value",)
|
||||
FUNCTION = "get_value"
|
||||
CATEGORY = "Utilitools/Constants"
|
||||
|
||||
def get_value(self, value):
|
||||
return (value,)
|
||||
|
||||
|
||||
class UtilConstantString:
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"value": ("STRING", {"default": "", "multiline": True}),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("STRING",)
|
||||
RETURN_NAMES = ("value",)
|
||||
FUNCTION = "get_value"
|
||||
CATEGORY = "Utilitools/Constants"
|
||||
|
||||
def get_value(self, value):
|
||||
return (value,)
|
||||
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
"UtilConstantInt": UtilConstantInt,
|
||||
"UtilConstantFloat": UtilConstantFloat,
|
||||
"UtilConstantString": UtilConstantString,
|
||||
}
|
||||
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"UtilConstantInt": "Constant Int",
|
||||
"UtilConstantFloat": "Constant Float",
|
||||
"UtilConstantString": "Constant String",
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
class UtilFloatToInt:
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"value": ("FLOAT", {"default": 0.0}),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("INT",)
|
||||
RETURN_NAMES = ("int_value",)
|
||||
FUNCTION = "convert"
|
||||
CATEGORY = "Utilitools/Conversion"
|
||||
|
||||
def convert(self, value):
|
||||
return (int(value),)
|
||||
|
||||
|
||||
class UtilIntToFloat:
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"value": ("INT", {"default": 0}),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("FLOAT",)
|
||||
RETURN_NAMES = ("float_value",)
|
||||
FUNCTION = "convert"
|
||||
CATEGORY = "Utilitools/Conversion"
|
||||
|
||||
def convert(self, value):
|
||||
return (float(value),)
|
||||
|
||||
|
||||
class UtilWhateverToString:
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {},
|
||||
"optional": {
|
||||
"int_value": ("INT", {"default": 0}),
|
||||
"float_value": ("FLOAT", {"default": 0.0}),
|
||||
"string_value": ("STRING", {"default": ""}),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("STRING",)
|
||||
RETURN_NAMES = ("string_result",)
|
||||
FUNCTION = "convert"
|
||||
CATEGORY = "Utilitools/Conversion"
|
||||
|
||||
def convert(self, int_value=None, float_value=None, string_value=None):
|
||||
if int_value is not None:
|
||||
return (str(int_value),)
|
||||
elif float_value is not None:
|
||||
return (str(float_value),)
|
||||
elif string_value is not None:
|
||||
return (str(string_value),)
|
||||
else:
|
||||
return ("",)
|
||||
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
"UtilFloatToInt": UtilFloatToInt,
|
||||
"UtilIntToFloat": UtilIntToFloat,
|
||||
"UtilWhateverToString": UtilWhateverToString,
|
||||
}
|
||||
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"UtilFloatToInt": "Float to Int",
|
||||
"UtilIntToFloat": "Int to Float",
|
||||
"UtilWhateverToString": "Whatever To String",
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
class UtilListCreate:
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {},
|
||||
"optional": {
|
||||
"item_0": ("*",),
|
||||
"item_1": ("*",),
|
||||
"item_2": ("*",),
|
||||
"item_3": ("*",),
|
||||
"item_4": ("*",),
|
||||
"item_5": ("*",),
|
||||
"item_6": ("*",),
|
||||
"item_7": ("*",),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("LIST",)
|
||||
RETURN_NAMES = ("list",)
|
||||
FUNCTION = "create_list"
|
||||
CATEGORY = "Utilitools/Data"
|
||||
|
||||
def create_list(self, **kwargs):
|
||||
items = []
|
||||
for i in range(8):
|
||||
key = f"item_{i}"
|
||||
if key in kwargs and kwargs[key] is not None:
|
||||
items.append(kwargs[key])
|
||||
return (items,)
|
||||
|
||||
|
||||
class UtilListIndex:
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"list": ("LIST",),
|
||||
"index": ("INT", {"default": 0, "min": 0, "max": 999, "step": 1}),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("*",)
|
||||
RETURN_NAMES = ("item",)
|
||||
FUNCTION = "get_item"
|
||||
CATEGORY = "Utilitools/Data"
|
||||
|
||||
def get_item(self, list, index):
|
||||
if index < len(list):
|
||||
return (list[index],)
|
||||
return (None,)
|
||||
|
||||
|
||||
class UtilBatchController:
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"input": ("*",),
|
||||
"batch_size": ("INT", {"default": 1, "min": 1, "max": 64, "step": 1}),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("*",)
|
||||
RETURN_NAMES = ("output",)
|
||||
FUNCTION = "control_batch"
|
||||
CATEGORY = "Utilitools/Data"
|
||||
|
||||
def control_batch(self, input, batch_size):
|
||||
if hasattr(input, 'shape') and len(input.shape) > 0:
|
||||
if input.shape[0] != batch_size:
|
||||
if input.shape[0] > batch_size:
|
||||
return (input[:batch_size],)
|
||||
else:
|
||||
import torch
|
||||
repeats = batch_size // input.shape[0] + (1 if batch_size % input.shape[0] > 0 else 0)
|
||||
repeated = input.repeat(repeats, *([1] * (len(input.shape) - 1)))
|
||||
return (repeated[:batch_size],)
|
||||
return (input,)
|
||||
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
"UtilListCreate": UtilListCreate,
|
||||
"UtilListIndex": UtilListIndex,
|
||||
"UtilBatchController": UtilBatchController,
|
||||
}
|
||||
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"UtilListCreate": "Create List",
|
||||
"UtilListIndex": "List Index",
|
||||
"UtilBatchController": "Batch Controller",
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
class UtilImageDimensions:
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"image": ("IMAGE",),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("INT", "INT", "INT")
|
||||
RETURN_NAMES = ("width", "height", "batch_size")
|
||||
FUNCTION = "get_dimensions"
|
||||
CATEGORY = "Utilitools/Image"
|
||||
|
||||
def get_dimensions(self, image):
|
||||
if len(image.shape) == 4: # [batch, height, width, channels]
|
||||
batch_size, height, width, channels = image.shape
|
||||
return (width, height, batch_size)
|
||||
elif len(image.shape) == 3: # [height, width, channels]
|
||||
height, width, channels = image.shape
|
||||
return (width, height, 1)
|
||||
else:
|
||||
return (0, 0, 0)
|
||||
|
||||
|
||||
class UtilAspectRatio:
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"width": ("INT", {"default": 512, "min": 1, "max": 8192, "step": 1}),
|
||||
"height": ("INT", {"default": 512, "min": 1, "max": 8192, "step": 1}),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("FLOAT", "STRING")
|
||||
RETURN_NAMES = ("ratio", "ratio_string")
|
||||
FUNCTION = "calculate_ratio"
|
||||
CATEGORY = "Utilitools/Image"
|
||||
|
||||
def calculate_ratio(self, width, height):
|
||||
import math
|
||||
|
||||
def gcd(a, b):
|
||||
while b:
|
||||
a, b = b, a % b
|
||||
return a
|
||||
|
||||
ratio = width / height
|
||||
divisor = gcd(width, height)
|
||||
simplified_width = width // divisor
|
||||
simplified_height = height // divisor
|
||||
|
||||
ratio_string = f"{simplified_width}:{simplified_height}"
|
||||
|
||||
return (ratio, ratio_string)
|
||||
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
"UtilImageDimensions": UtilImageDimensions,
|
||||
"UtilAspectRatio": UtilAspectRatio,
|
||||
}
|
||||
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"UtilImageDimensions": "Image Dimensions",
|
||||
"UtilAspectRatio": "Aspect Ratio",
|
||||
}
|
||||
+122
@@ -0,0 +1,122 @@
|
||||
class UtilIfThenElse:
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"condition": ("BOOLEAN", {"default": True}),
|
||||
"if_true": ("*",),
|
||||
"if_false": ("*",),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("*",)
|
||||
RETURN_NAMES = ("result",)
|
||||
FUNCTION = "execute"
|
||||
CATEGORY = "Utilitools/Logic"
|
||||
|
||||
def execute(self, condition, if_true, if_false):
|
||||
return (if_true if condition else if_false,)
|
||||
|
||||
|
||||
class UtilSwitch:
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"index": ("INT", {"default": 0, "min": 0, "max": 7, "step": 1}),
|
||||
},
|
||||
"optional": {
|
||||
"input_0": ("*",),
|
||||
"input_1": ("*",),
|
||||
"input_2": ("*",),
|
||||
"input_3": ("*",),
|
||||
"input_4": ("*",),
|
||||
"input_5": ("*",),
|
||||
"input_6": ("*",),
|
||||
"input_7": ("*",),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("*",)
|
||||
RETURN_NAMES = ("selected",)
|
||||
FUNCTION = "switch"
|
||||
CATEGORY = "Utilitools/Logic"
|
||||
|
||||
def switch(self, index, **kwargs):
|
||||
input_key = f"input_{index}"
|
||||
if input_key in kwargs and kwargs[input_key] is not None:
|
||||
return (kwargs[input_key],)
|
||||
return (None,)
|
||||
|
||||
|
||||
class UtilBooleanAND:
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"a": ("BOOLEAN", {"default": True}),
|
||||
"b": ("BOOLEAN", {"default": True}),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("BOOLEAN",)
|
||||
RETURN_NAMES = ("result",)
|
||||
FUNCTION = "execute"
|
||||
CATEGORY = "Utilitools/Logic"
|
||||
|
||||
def execute(self, a, b):
|
||||
return (a and b,)
|
||||
|
||||
|
||||
class UtilBooleanOR:
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"a": ("BOOLEAN", {"default": False}),
|
||||
"b": ("BOOLEAN", {"default": False}),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("BOOLEAN",)
|
||||
RETURN_NAMES = ("result",)
|
||||
FUNCTION = "execute"
|
||||
CATEGORY = "Utilitools/Logic"
|
||||
|
||||
def execute(self, a, b):
|
||||
return (a or b,)
|
||||
|
||||
|
||||
class UtilBooleanNOT:
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"value": ("BOOLEAN", {"default": True}),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("BOOLEAN",)
|
||||
RETURN_NAMES = ("result",)
|
||||
FUNCTION = "execute"
|
||||
CATEGORY = "Utilitools/Logic"
|
||||
|
||||
def execute(self, value):
|
||||
return (not value,)
|
||||
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
"UtilIfThenElse": UtilIfThenElse,
|
||||
"UtilSwitch": UtilSwitch,
|
||||
"UtilBooleanAND": UtilBooleanAND,
|
||||
"UtilBooleanOR": UtilBooleanOR,
|
||||
"UtilBooleanNOT": UtilBooleanNOT,
|
||||
}
|
||||
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"UtilIfThenElse": "If Then Else",
|
||||
"UtilSwitch": "Switch",
|
||||
"UtilBooleanAND": "Boolean AND",
|
||||
"UtilBooleanOR": "Boolean OR",
|
||||
"UtilBooleanNOT": "Boolean NOT",
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
"""
|
||||
ComfyUI Utilitools - Main Node Registry
|
||||
|
||||
Centralized registration of all utility nodes organized by category.
|
||||
"""
|
||||
|
||||
from .math import NODE_CLASS_MAPPINGS as MATH_MAPPINGS, NODE_DISPLAY_NAME_MAPPINGS as MATH_DISPLAY_MAPPINGS
|
||||
from .text import NODE_CLASS_MAPPINGS as TEXT_MAPPINGS, NODE_DISPLAY_NAME_MAPPINGS as TEXT_DISPLAY_MAPPINGS
|
||||
from .conversion import NODE_CLASS_MAPPINGS as CONVERSION_MAPPINGS, NODE_DISPLAY_NAME_MAPPINGS as CONVERSION_DISPLAY_MAPPINGS
|
||||
from .logic import NODE_CLASS_MAPPINGS as LOGIC_MAPPINGS, NODE_DISPLAY_NAME_MAPPINGS as LOGIC_DISPLAY_MAPPINGS
|
||||
from .data import NODE_CLASS_MAPPINGS as DATA_MAPPINGS, NODE_DISPLAY_NAME_MAPPINGS as DATA_DISPLAY_MAPPINGS
|
||||
from .workflow import NODE_CLASS_MAPPINGS as WORKFLOW_MAPPINGS, NODE_DISPLAY_NAME_MAPPINGS as WORKFLOW_DISPLAY_MAPPINGS
|
||||
from .constants import NODE_CLASS_MAPPINGS as CONSTANTS_MAPPINGS, NODE_DISPLAY_NAME_MAPPINGS as CONSTANTS_DISPLAY_MAPPINGS
|
||||
from .image import NODE_CLASS_MAPPINGS as IMAGE_MAPPINGS, NODE_DISPLAY_NAME_MAPPINGS as IMAGE_DISPLAY_MAPPINGS
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
**MATH_MAPPINGS,
|
||||
**TEXT_MAPPINGS,
|
||||
**CONVERSION_MAPPINGS,
|
||||
**LOGIC_MAPPINGS,
|
||||
**DATA_MAPPINGS,
|
||||
**WORKFLOW_MAPPINGS,
|
||||
**CONSTANTS_MAPPINGS,
|
||||
**IMAGE_MAPPINGS,
|
||||
}
|
||||
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
**MATH_DISPLAY_MAPPINGS,
|
||||
**TEXT_DISPLAY_MAPPINGS,
|
||||
**CONVERSION_DISPLAY_MAPPINGS,
|
||||
**LOGIC_DISPLAY_MAPPINGS,
|
||||
**DATA_DISPLAY_MAPPINGS,
|
||||
**WORKFLOW_DISPLAY_MAPPINGS,
|
||||
**CONSTANTS_DISPLAY_MAPPINGS,
|
||||
**IMAGE_DISPLAY_MAPPINGS,
|
||||
}
|
||||
+130
@@ -0,0 +1,130 @@
|
||||
"""
|
||||
Mathematical utility nodes for ComfyUI.
|
||||
|
||||
Provides basic arithmetic operations with both integer and float outputs.
|
||||
"""
|
||||
|
||||
|
||||
class UtilAdd:
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"a": ("FLOAT", {"default": 0.0, "min": -999999.0, "max": 999999.0, "step": 0.001}),
|
||||
"b": ("FLOAT", {"default": 0.0, "min": -999999.0, "max": 999999.0, "step": 0.001}),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("INT", "FLOAT")
|
||||
RETURN_NAMES = ("int_result", "float_result")
|
||||
FUNCTION = "add"
|
||||
CATEGORY = "Utilitools/Math"
|
||||
|
||||
def add(self, a, b):
|
||||
result = a + b
|
||||
return (int(result), float(result))
|
||||
|
||||
|
||||
class UtilSubtract:
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"a": ("FLOAT", {"default": 0.0, "min": -999999.0, "max": 999999.0, "step": 0.001}),
|
||||
"b": ("FLOAT", {"default": 0.0, "min": -999999.0, "max": 999999.0, "step": 0.001}),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("INT", "FLOAT")
|
||||
RETURN_NAMES = ("int_result", "float_result")
|
||||
FUNCTION = "subtract"
|
||||
CATEGORY = "Utilitools/Math"
|
||||
|
||||
def subtract(self, a, b):
|
||||
result = a - b
|
||||
return (int(result), float(result))
|
||||
|
||||
|
||||
class UtilMultiply:
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"a": ("FLOAT", {"default": 1.0, "min": -999999.0, "max": 999999.0, "step": 0.001}),
|
||||
"b": ("FLOAT", {"default": 1.0, "min": -999999.0, "max": 999999.0, "step": 0.001}),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("INT", "FLOAT")
|
||||
RETURN_NAMES = ("int_result", "float_result")
|
||||
FUNCTION = "multiply"
|
||||
CATEGORY = "Utilitools/Math"
|
||||
|
||||
def multiply(self, a, b):
|
||||
result = a * b
|
||||
return (int(result), float(result))
|
||||
|
||||
|
||||
class UtilDivide:
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"a": ("FLOAT", {"default": 1.0, "min": -999999.0, "max": 999999.0, "step": 0.001}),
|
||||
"b": ("FLOAT", {"default": 1.0, "min": -999999.0, "max": 999999.0, "step": 0.001}),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("INT", "FLOAT")
|
||||
RETURN_NAMES = ("int_result", "float_result")
|
||||
FUNCTION = "divide"
|
||||
CATEGORY = "Utilitools/Math"
|
||||
|
||||
def divide(self, a, b):
|
||||
if b == 0:
|
||||
return (0, 0.0)
|
||||
result = a / b
|
||||
return (int(result), float(result))
|
||||
|
||||
|
||||
class UtilCalculator:
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"expression": ("STRING", {"default": "2 + 2"}),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("FLOAT", "INT")
|
||||
RETURN_NAMES = ("float_result", "int_result")
|
||||
FUNCTION = "calculate"
|
||||
CATEGORY = "Utilitools/Math"
|
||||
|
||||
def calculate(self, expression):
|
||||
try:
|
||||
import re
|
||||
allowed_chars = re.compile(r'^[0-9+\-*/().\s]+$')
|
||||
if not allowed_chars.match(expression):
|
||||
return (0.0, 0)
|
||||
result = eval(expression)
|
||||
return (float(result), int(result))
|
||||
except:
|
||||
return (0.0, 0)
|
||||
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
"UtilAdd": UtilAdd,
|
||||
"UtilSubtract": UtilSubtract,
|
||||
"UtilMultiply": UtilMultiply,
|
||||
"UtilDivide": UtilDivide,
|
||||
"UtilCalculator": UtilCalculator,
|
||||
}
|
||||
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"UtilAdd": "Add",
|
||||
"UtilSubtract": "Subtract",
|
||||
"UtilMultiply": "Multiply",
|
||||
"UtilDivide": "Divide",
|
||||
"UtilCalculator": "Calculator",
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
class UtilTextConcat:
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"text_a": ("STRING", {"default": ""}),
|
||||
"text_b": ("STRING", {"default": ""}),
|
||||
},
|
||||
"optional": {
|
||||
"separator": ("STRING", {"default": ""}),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("STRING",)
|
||||
RETURN_NAMES = ("result",)
|
||||
FUNCTION = "concat"
|
||||
CATEGORY = "Utilitools/Text"
|
||||
|
||||
def concat(self, text_a, text_b, separator=""):
|
||||
return (text_a + separator + text_b,)
|
||||
|
||||
|
||||
class UtilStringReplace:
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"text": ("STRING", {"default": ""}),
|
||||
"find": ("STRING", {"default": ""}),
|
||||
"replace": ("STRING", {"default": ""}),
|
||||
},
|
||||
"optional": {
|
||||
"count": ("INT", {"default": -1, "min": -1, "max": 1000, "step": 1}),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("STRING",)
|
||||
RETURN_NAMES = ("result",)
|
||||
FUNCTION = "replace"
|
||||
CATEGORY = "Utilitools/Text"
|
||||
|
||||
def replace(self, text, find, replace, count=-1):
|
||||
if count == -1:
|
||||
return (text.replace(find, replace),)
|
||||
else:
|
||||
return (text.replace(find, replace, count),)
|
||||
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
"UtilTextConcat": UtilTextConcat,
|
||||
"UtilStringReplace": UtilStringReplace,
|
||||
}
|
||||
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"UtilTextConcat": "Text Concatenation",
|
||||
"UtilStringReplace": "String Replace",
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
class UtilPassthrough:
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"input": ("*",),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("*",)
|
||||
RETURN_NAMES = ("output",)
|
||||
FUNCTION = "passthrough"
|
||||
CATEGORY = "Utilitools/Workflow"
|
||||
|
||||
def passthrough(self, input):
|
||||
return (input,)
|
||||
|
||||
|
||||
class UtilCounter:
|
||||
def __init__(self):
|
||||
self.counter = 0
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"increment": ("INT", {"default": 1, "min": -999, "max": 999, "step": 1}),
|
||||
},
|
||||
"optional": {
|
||||
"reset": ("BOOLEAN", {"default": False}),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("INT",)
|
||||
RETURN_NAMES = ("count",)
|
||||
FUNCTION = "count"
|
||||
CATEGORY = "Utilitools/Workflow"
|
||||
|
||||
def count(self, increment, reset=False):
|
||||
if reset:
|
||||
self.counter = 0
|
||||
self.counter += increment
|
||||
return (self.counter,)
|
||||
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
"UtilPassthrough": UtilPassthrough,
|
||||
"UtilCounter": UtilCounter,
|
||||
}
|
||||
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"UtilPassthrough": "Passthrough",
|
||||
"UtilCounter": "Counter",
|
||||
}
|
||||
Reference in New Issue
Block a user