Files
MDMAchine-ComfyUI_MD_Nodes/math/MD_BasicMath.py
T

311 lines
11 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
# █▓▒░ ░▒▓█
# █▓▒░ MD_Nodes/BasicMath – Basic Arithmetic Suite v1.1.0 ░▒▓█
# █▓▒░ ░▒▓█
# ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
# ╠═ © 2026 MDMAchine
# ╠═ License: GNU General Public License v3.0 (GPL v3)
# ║
# ║ This program is free software: you can redistribute it and/or modify
# ║ it under the terms of the GNU General Public License as published by
# ║ the Free Software Foundation, either version 3 of the License, or
# ║ (at your option) any later version.
# ║
# ║ This program is distributed in the hope that it will be useful,
# ║ but WITHOUT ANY WARRANTY; without even the implied warranty of
# ║ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# ║ GNU General Public License for more details.
# ║
# ║ You should have received a copy of the GNU General Public License
# ║ along with this program. If not, see <https://www.gnu.org/licenses/>.
# ╚════════════════════════════════════════════════════════════════════════════
# =================================================================================
# == Standard Library Imports
# =================================================================================
VERSION = "v1.1.0" # UPS v1.5.8
import math
# =================================================================================
# == Configuration Constants
# =================================================================================
import logging
CONST_FLOAT_MIN = -1.797e+308
CONST_FLOAT_MAX = 1.797e+308
CONST_INT_MIN = -9223372036854775808
CONST_INT_MAX = 9223372036854775807
# =================================================================================
# == Node Classes
# =================================================================================
class MD_Math_Add:
"""
Performs basic addition (A + B).
Returns both INT and FLOAT for workflow flexibility.
"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"a": ("FLOAT", {
"default": 0.0,
"min": CONST_FLOAT_MIN,
"max": CONST_FLOAT_MAX,
"step": 0.01,
"tooltip": (
"VALUE A (PRIMARY)\n"
"• Purpose: The first number to add.\n"
"• Range: Full float range.\n"
"\n⭐ Connect any INT or FLOAT output here."
)
}),
"b": ("FLOAT", {
"default": 0.0,
"min": CONST_FLOAT_MIN,
"max": CONST_FLOAT_MAX,
"step": 0.01,
"tooltip": (
"VALUE B (ADDEND)\n"
"• Purpose: The value added to A.\n"
"• Range: Full float range.\n"
"\n⭐ Standard addition operation."
)
}),
},
}
RETURN_TYPES = ("INT", "FLOAT")
RETURN_NAMES = ("int_result", "float_result")
FUNCTION = "op_add"
CATEGORY = "MD_Nodes/Math"
def op_add(self, a, b):
result = a + b
return (int(result), float(result))
class MD_Math_Subtract:
"""
Performs basic subtraction (A - B).
Returns both INT and FLOAT for workflow flexibility.
"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"a": ("FLOAT", {
"default": 0.0,
"min": CONST_FLOAT_MIN,
"max": CONST_FLOAT_MAX,
"step": 0.01,
"tooltip": (
"VALUE A (PRIMARY)\n"
"• Purpose: The starting number.\n"
"• Range: Full float range.\n"
"\n⭐ The number being subtracted FROM."
)
}),
"b": ("FLOAT", {
"default": 0.0,
"min": CONST_FLOAT_MIN,
"max": CONST_FLOAT_MAX,
"step": 0.01,
"tooltip": (
"VALUE B (SUBTRAHEND)\n"
"• Purpose: The amount to remove from A.\n"
"• Range: Full float range.\n"
"\n⭐ Logic: Result = A - B"
)
}),
},
}
RETURN_TYPES = ("INT", "FLOAT")
RETURN_NAMES = ("int_result", "float_result")
FUNCTION = "op_sub"
CATEGORY = "MD_Nodes/Math"
def op_sub(self, a, b):
result = a - b
return (int(result), float(result))
class MD_Math_Multiply:
"""
Performs basic multiplication (A * B).
Returns both INT and FLOAT for workflow flexibility.
"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"a": ("FLOAT", {
"default": 1.0,
"min": CONST_FLOAT_MIN,
"max": CONST_FLOAT_MAX,
"step": 0.01,
"tooltip": (
"VALUE A (PRIMARY)\n"
"• Purpose: The first factor.\n"
"• Range: Full float range.\n"
"\n⭐ Base value for scaling."
)
}),
"b": ("FLOAT", {
"default": 1.0,
"min": CONST_FLOAT_MIN,
"max": CONST_FLOAT_MAX,
"step": 0.01,
"tooltip": (
"VALUE B (MULTIPLIER)\n"
"• Purpose: The scaling factor.\n"
"• Range: Full float range.\n"
"\n⭐ Recommended: 2.0 (Double), 0.5 (Half)."
)
}),
},
}
RETURN_TYPES = ("INT", "FLOAT")
RETURN_NAMES = ("int_result", "float_result")
FUNCTION = "op_mul"
CATEGORY = "MD_Nodes/Math"
def op_mul(self, a, b):
result = a * b
return (int(result), float(result))
class MD_Math_Divide:
"""
Performs basic division (A / B).
Includes safety check for division by zero.
"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"a": ("FLOAT", {
"default": 1.0,
"min": CONST_FLOAT_MIN,
"max": CONST_FLOAT_MAX,
"step": 0.01,
"tooltip": (
"VALUE A (NUMERATOR)\n"
"• Purpose: The value to be divided.\n"
"• Range: Full float range.\n"
"\n⭐ The top number in the fraction."
)
}),
"b": ("FLOAT", {
"default": 1.0,
"min": CONST_FLOAT_MIN,
"max": CONST_FLOAT_MAX,
"step": 0.01,
"tooltip": (
"VALUE B (DENOMINATOR)\n"
"• Purpose: What to divide by.\n"
"• Trade-offs: If 0 is provided, returns 0.0 to prevent crash.\n"
"\n⭐ Logic: Result = A / B"
)
}),
},
}
RETURN_TYPES = ("INT", "FLOAT")
RETURN_NAMES = ("int_result", "float_result")
FUNCTION = "op_div"
CATEGORY = "MD_Nodes/Math"
def op_div(self, a, b):
if b == 0:
logging.warning("⚠️ [MD_Nodes] Warning: Division by zero detected. Returning 0.")
return (0, 0.0)
result = a / b
return (int(result), float(result))
# =================================================================================
# == Node Registration
# =================================================================================
NODE_CLASS_MAPPINGS = {
"MD_Math_Add": MD_Math_Add,
"MD_Math_Subtract": MD_Math_Subtract,
"MD_Math_Multiply": MD_Math_Multiply,
"MD_Math_Divide": MD_Math_Divide,
}
NODE_DISPLAY_NAME_MAPPINGS = {
"MD_Math_Add": "MD: Math Add (Int/Float)",
"MD_Math_Subtract": "MD: Math Subtract (Int/Float)",
"MD_Math_Multiply": "MD: Math Multiply (Int/Float)",
"MD_Math_Divide": "MD: Math Divide (Int/Float)",
}
# =================================================================================
# == Development & Testing
# =================================================================================
if __name__ == "__main__":
logging.info("🧪 Running Self-Tests for MD_BasicMath...")
test_passed = 0
test_failed = 0
try:
node_add = MD_Math_Add()
res_add = node_add.op_add(5.5, 4.5)
assert res_add == (10, 10.0), f"Add failed: {res_add}"
logging.info("✅ Addition Check: PASSED")
test_passed += 1
except AssertionError as e:
logging.error(f"❌ Addition Check: FAILED - {e}")
test_failed += 1
try:
node_sub = MD_Math_Subtract()
res_sub = node_sub.op_sub(10.0, 4.5)
assert res_sub == (5, 5.5), f"Sub failed: {res_sub}"
logging.info("✅ Subtraction Check: PASSED")
test_passed += 1
except AssertionError as e:
logging.error(f"❌ Subtraction Check: FAILED - {e}")
test_failed += 1
try:
node_mul = MD_Math_Multiply()
res_mul = node_mul.op_mul(2.0, 3.5)
assert res_mul == (7, 7.0), f"Mul failed: {res_mul}"
logging.info("✅ Multiplication Check: PASSED")
test_passed += 1
except AssertionError as e:
logging.error(f"❌ Multiplication Check: FAILED - {e}")
test_failed += 1
try:
node_div = MD_Math_Divide()
res_div = node_div.op_div(10.0, 2.0)
assert res_div == (5, 5.0), f"Div failed: {res_div}"
logging.info("✅ Division Check: PASSED")
test_passed += 1
except AssertionError as e:
logging.error(f"❌ Division Check: FAILED - {e}")
test_failed += 1
try:
res_div_zero = node_div.op_div(10.0, 0.0)
assert res_div_zero == (0, 0.0), f"DivZero failed: {res_div_zero}"
logging.info("✅ Division Zero Safety: PASSED")
test_passed += 1
except AssertionError as e:
logging.error(f"❌ Division Zero Safety: FAILED - {e}")
test_failed += 1
logging.info(f"\n{'='*60}")
logging.info(f"Test Results: {test_passed} passed, {test_failed} failed")
logging.info(f"{'='*60}")