Add nodes for basic logic operation and math

This commit is contained in:
Terry Jia 2025-10-23 08:29:08 -04:00
parent a1864c01f2
commit 4a9838c3ef
3 changed files with 576 additions and 0 deletions

108
comfy_extras/nodes_logic.py Normal file
View File

@ -0,0 +1,108 @@
from comfy.comfy_types.node_typing import IO, ComfyNodeABC
class LogicIF(ComfyNodeABC):
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"if_condition": (IO.BOOLEAN, {}),
"when_true": (IO.ANY, {})
},
"optional": {
"when_false": (IO.ANY, {})
}
}
RETURN_TYPES = (IO.ANY,)
RETURN_NAMES = ("result",)
FUNCTION = "execute"
CATEGORY = "utils/logic"
def execute(self, if_condition, when_true, when_false, **kwargs):
if if_condition:
return when_true,
else:
return when_false,
class LogicAND(ComfyNodeABC):
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"input_a": (IO.BOOLEAN, {}),
"input_b": (IO.BOOLEAN, {})
}
}
RETURN_TYPES = (IO.BOOLEAN,)
FUNCTION = "execute"
CATEGORY = "utils/logic"
def execute(self, input_a, input_b, **kwargs):
return input_a and input_b,
class LogicOR(ComfyNodeABC):
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"input_a": (IO.BOOLEAN, {}),
"input_b": (IO.BOOLEAN, {})
}
}
RETURN_TYPES = (IO.BOOLEAN,)
FUNCTION = "execute"
CATEGORY = "utils/logic"
def execute(self, input_a, input_b, **kwargs):
return input_a or input_b,
class LogicNOT(ComfyNodeABC):
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"input": (IO.BOOLEAN, {})
}
}
RETURN_TYPES = (IO.BOOLEAN,)
FUNCTION = "execute"
CATEGORY = "utils/logic"
def execute(self, input, **kwargs):
return not input,
class LogicXOR(ComfyNodeABC):
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"input_a": (IO.BOOLEAN, {}),
"input_b": (IO.BOOLEAN, {})
}
}
RETURN_TYPES = (IO.BOOLEAN,)
FUNCTION = "execute"
CATEGORY = "utils/logic"
def execute(self, input_a, input_b, **kwargs):
return input_a != input_b,
NODE_CLASS_MAPPINGS = {
"LogicIF": LogicIF,
"LogicAND": LogicAND,
"LogicOR": LogicOR,
"LogicNOT": LogicNOT,
"LogicXOR": LogicXOR
}
NODE_DISPLAY_NAME_MAPPINGS = {
"LogicIF": "IF",
"LogicAND": "AND",
"LogicOR": "OR",
"LogicNOT": "NOT",
"LogicXOR": "XOR"
}

466
comfy_extras/nodes_math.py Normal file
View File

@ -0,0 +1,466 @@
from comfy.comfy_types.node_typing import IO, ComfyNodeABC
import math
class MathAdd(ComfyNodeABC):
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"value_a": (IO.NUMBER, {}),
"value_b": (IO.NUMBER, {})
}
}
RETURN_TYPES = (IO.NUMBER,)
RETURN_NAMES = ("result",)
FUNCTION = "execute"
CATEGORY = "utils/math"
def execute(self, value_a, value_b, **kwargs):
return value_a + value_b,
class MathSubtract(ComfyNodeABC):
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"value_a": (IO.NUMBER, {}),
"value_b": (IO.NUMBER, {})
}
}
RETURN_TYPES = (IO.NUMBER,)
RETURN_NAMES = ("result",)
FUNCTION = "execute"
CATEGORY = "utils/math"
def execute(self, value_a, value_b, **kwargs):
return value_a - value_b,
class MathMultiply(ComfyNodeABC):
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"value_a": (IO.NUMBER, {}),
"value_b": (IO.NUMBER, {})
}
}
RETURN_TYPES = (IO.NUMBER,)
RETURN_NAMES = ("result",)
FUNCTION = "execute"
CATEGORY = "utils/math"
def execute(self, value_a, value_b, **kwargs):
return value_a * value_b,
class MathDivide(ComfyNodeABC):
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"value_a": (IO.NUMBER, {}),
"value_b": (IO.NUMBER, {}),
"handle_zero": (IO.BOOLEAN, {"default": True})
}
}
RETURN_TYPES = (IO.NUMBER,)
RETURN_NAMES = ("result",)
FUNCTION = "execute"
CATEGORY = "utils/math"
def execute(self, value_a, value_b, handle_zero, **kwargs):
if value_b == 0:
if handle_zero:
return 0,
else:
raise ValueError("Division by zero")
return value_a / value_b,
class MathPower(ComfyNodeABC):
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"base": (IO.NUMBER, {}),
"exponent": (IO.NUMBER, {})
}
}
RETURN_TYPES = (IO.NUMBER,)
RETURN_NAMES = ("result",)
FUNCTION = "execute"
CATEGORY = "utils/math"
def execute(self, base, exponent, **kwargs):
return base ** exponent,
class MathFloor(ComfyNodeABC):
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"value": (IO.NUMBER, {}),
}
}
RETURN_TYPES = (IO.NUMBER,)
RETURN_NAMES = ("result",)
FUNCTION = "execute"
CATEGORY = "utils/math"
def execute(self, value, **kwargs):
return math.floor(value),
class MathNumberConvert(ComfyNodeABC):
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"number_value": (IO.NUMBER, {}),
}
}
RETURN_TYPES = (IO.INT, IO.FLOAT,)
RETURN_NAMES = ("result_int", "result_float",)
FUNCTION = "execute"
CATEGORY = "utils/math"
def execute(self, number_value, **kwargs):
return int(number_value), float(number_value)
class MathCeil(ComfyNodeABC):
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"value": (IO.NUMBER, {})
}
}
RETURN_TYPES = (IO.NUMBER,)
RETURN_NAMES = ("result",)
FUNCTION = "execute"
CATEGORY = "utils/math"
def execute(self, value, **kwargs):
return math.ceil(value),
class MathRound(ComfyNodeABC):
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"value": (IO.NUMBER, {}),
"decimals": (IO.INT, {"default": 0, "min": 0, "max": 10})
}
}
RETURN_TYPES = (IO.NUMBER,)
RETURN_NAMES = ("result",)
FUNCTION = "execute"
CATEGORY = "utils/math"
def execute(self, value, decimals, **kwargs):
return round(value, decimals),
class MathModulo(ComfyNodeABC):
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"value_a": (IO.NUMBER, {}),
"value_b": (IO.NUMBER, {}),
}
}
RETURN_TYPES = (IO.NUMBER,)
RETURN_NAMES = ("result",)
FUNCTION = "execute"
CATEGORY = "utils/math"
def execute(self, value_a, value_b, **kwargs):
if value_b == 0:
return 0,
return value_a % value_b,
class MathAbs(ComfyNodeABC):
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"value": (IO.NUMBER, {})
}
}
RETURN_TYPES = (IO.NUMBER,)
RETURN_NAMES = ("result",)
FUNCTION = "execute"
CATEGORY = "utils/math"
def execute(self, value, **kwargs):
return abs(value),
class MathSqrt(ComfyNodeABC):
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"value": (IO.NUMBER, {})
}
}
RETURN_TYPES = (IO.NUMBER,)
RETURN_NAMES = ("result",)
FUNCTION = "execute"
CATEGORY = "utils/math"
def execute(self, value, **kwargs):
if value < 0:
return 0,
return math.sqrt(value),
class MathSin(ComfyNodeABC):
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"angle": (IO.NUMBER, {}),
"unit": (IO.COMBO, {"options": ["Radians", "Degrees"]})
}
}
RETURN_TYPES = (IO.NUMBER,)
RETURN_NAMES = ("result",)
FUNCTION = "execute"
CATEGORY = "utils/math/trigonometry"
def execute(self, angle, unit, **kwargs):
if unit == "Degrees":
angle = math.radians(angle)
return math.sin(angle),
class MathCos(ComfyNodeABC):
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"angle": (IO.NUMBER, {}),
"unit": (IO.COMBO, {"options": ["Radians", "Degrees"]})
}
}
RETURN_TYPES = (IO.NUMBER,)
RETURN_NAMES = ("result",)
FUNCTION = "execute"
CATEGORY = "utils/math/trigonometry"
def execute(self, angle, unit, **kwargs):
if unit == "Degrees":
angle = math.radians(angle)
return math.cos(angle),
class MathTan(ComfyNodeABC):
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"angle": (IO.NUMBER, {}),
"unit": (IO.COMBO, {"options": ["Radians", "Degrees"]})
}
}
RETURN_TYPES = (IO.NUMBER,)
RETURN_NAMES = ("result",)
FUNCTION = "execute"
CATEGORY = "utils/math/trigonometry"
def execute(self, angle, unit, **kwargs):
if unit == "Degrees":
angle = math.radians(angle)
return math.tan(angle),
class MathMin(ComfyNodeABC):
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"value_a": (IO.NUMBER, {}),
"value_b": (IO.NUMBER, {})
}
}
RETURN_TYPES = (IO.NUMBER,)
RETURN_NAMES = ("result",)
FUNCTION = "execute"
CATEGORY = "utils/math"
def execute(self, value_a, value_b, **kwargs):
return min(value_a, value_b),
class MathMax(ComfyNodeABC):
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"value_a": (IO.NUMBER, {}),
"value_b": (IO.NUMBER, {})
}
}
RETURN_TYPES = (IO.NUMBER,)
RETURN_NAMES = ("result",)
FUNCTION = "execute"
CATEGORY = "utils/math"
def execute(self, value_a, value_b, **kwargs):
return max(value_a, value_b),
class MathClamp(ComfyNodeABC):
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"value": (IO.NUMBER, {}),
"min_value": (IO.NUMBER, {}),
"max_value": (IO.NUMBER, {})
}
}
RETURN_TYPES = (IO.NUMBER,)
RETURN_NAMES = ("result",)
FUNCTION = "execute"
CATEGORY = "utils/math"
def execute(self, value, min_value, max_value, **kwargs):
return max(min(value, max_value), min_value),
class StringToNumber(ComfyNodeABC):
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"string": (IO.STRING, {"multiline": False})
},
"optional": {
"default_value": (IO.NUMBER, {})
}
}
RETURN_TYPES = (IO.NUMBER,)
RETURN_NAMES = ("result",)
FUNCTION = "execute"
CATEGORY = "utils/math"
def execute(self, string, **kwargs):
default_value = kwargs.get("default_value")
if default_value is None:
default_value = 0
try:
if '.' in string:
return float(string),
else:
return int(string),
except (ValueError, TypeError):
return default_value,
class NumberToString(ComfyNodeABC):
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"number": (IO.NUMBER, {})
}
}
RETURN_TYPES = (IO.STRING,)
RETURN_NAMES = ("result",)
FUNCTION = "execute"
CATEGORY = "utils/math"
def execute(self, number, **kwargs):
return str(number),
class MathCompare(ComfyNodeABC):
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"value_a": (IO.NUMBER, {}),
"value_b": (IO.NUMBER, {}),
"comparison": (IO.COMBO, {"options": ["Equal", "Not Equal", "Greater Than", "Less Than", "Greater Than or Equal", "Less Than or Equal"]})
}
}
RETURN_TYPES = (IO.BOOLEAN,)
RETURN_NAMES = ("result",)
FUNCTION = "execute"
CATEGORY = "utils/math"
def execute(self, value_a, value_b, comparison, **kwargs):
if comparison == "Equal":
return value_a == value_b,
elif comparison == "Not Equal":
return value_a != value_b,
elif comparison == "Greater Than":
return value_a > value_b,
elif comparison == "Less Than":
return value_a < value_b,
elif comparison == "Greater Than or Equal":
return value_a >= value_b,
elif comparison == "Less Than or Equal":
return value_a <= value_b,
else:
return False,
NODE_CLASS_MAPPINGS = {
"MathAdd": MathAdd,
"MathSubtract": MathSubtract,
"MathMultiply": MathMultiply,
"MathDivide": MathDivide,
"MathPower": MathPower,
"MathFloor": MathFloor,
"MathCeil": MathCeil,
"MathRound": MathRound,
"MathModulo": MathModulo,
"MathAbs": MathAbs,
"MathSqrt": MathSqrt,
"MathSin": MathSin,
"MathCos": MathCos,
"MathTan": MathTan,
"MathMin": MathMin,
"MathMax": MathMax,
"MathClamp": MathClamp,
"MathNumberConvert": MathNumberConvert,
"StringToNumber": StringToNumber,
"NumberToString": NumberToString,
"MathCompare": MathCompare
}
NODE_DISPLAY_NAME_MAPPINGS = {
"MathAdd": "Add",
"MathSubtract": "Subtract",
"MathMultiply": "Multiply",
"MathDivide": "Divide",
"MathPower": "Power",
"MathFloor": "Floor",
"MathCeil": "Ceil",
"MathRound": "Round",
"MathModulo": "Modulo",
"MathAbs": "Absolute",
"MathSqrt": "Square Root",
"MathSin": "Sine",
"MathCos": "Cosine",
"MathTan": "Tangent",
"MathMin": "Minimum",
"MathMax": "Maximum",
"MathClamp": "Clamp",
"MathNumberConvert": "Number Convert",
"StringToNumber": "String To Number",
"NumberToString": "Number To String",
"MathCompare": "Compare"
}

View File

@ -2329,6 +2329,8 @@ async def init_builtin_extra_nodes():
"nodes_model_patch.py",
"nodes_easycache.py",
"nodes_audio_encoder.py",
"nodes_logic.py",
"nodes_math.py",
]
import_failed = []