Add math operations and min/max for values and lists
This commit is contained in:
@@ -28,7 +28,7 @@ These nodes are very lightweight and require no additional dependencies.
|
||||
- Modification: append, extend, insert, remove, pop, clear, set_item
|
||||
- Access: get_item, slice, index, contains
|
||||
- Information: length, count
|
||||
- Operations: sort, reverse
|
||||
- Operations: sort, reverse, min, max
|
||||
|
||||
- **SET**: Python set manipulation nodes (as a single variable)
|
||||
- Conversion: convert to data list, to LIST
|
||||
@@ -44,7 +44,7 @@ These nodes are very lightweight and require no additional dependencies.
|
||||
- Modification: append, filter, extend, insert, remove, pop, clear, set_item
|
||||
- Access: get_item, slice, index, contains
|
||||
- Information: length, count
|
||||
- Operations: sort, reverse, copy, zip
|
||||
- Operations: sort, reverse, copy, zip, min, max
|
||||
|
||||
- **DICT**: Dictionary manipulation nodes
|
||||
- Creation: create, from_items, from_lists, fromkeys, any_to_DICT
|
||||
@@ -76,6 +76,15 @@ These nodes are very lightweight and require no additional dependencies.
|
||||
- Conditional: if/else for branching logic based on conditions
|
||||
- Selection: switch/case for selecting from multiple options based on an index
|
||||
|
||||
- **Math**: Mathematical operations
|
||||
- Trigonometric: sin, cos, tan, asin, acos, atan, atan2
|
||||
- Logarithmic/Exponential: log, log10, exp, sqrt
|
||||
- Numerical: min, max
|
||||
- Constants: pi, e
|
||||
- Conversion: degrees/radians
|
||||
- Rounding: floor, ceil
|
||||
- Other: abs (absolute value)
|
||||
|
||||
## Understanding LIST vs. data list vs. SET
|
||||
|
||||
ComfyUI has different data types that serve different purposes:
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
from . import (boolean_nodes, comparison_nodes, control_flow, data_list_nodes, dict_nodes,
|
||||
float_nodes, int_nodes, list_nodes, set_nodes, string_nodes)
|
||||
float_nodes, int_nodes, list_nodes, math_nodes, set_nodes, string_nodes)
|
||||
|
||||
NODE_CLASS_MAPPINGS = {}
|
||||
NODE_CLASS_MAPPINGS.update(boolean_nodes.NODE_CLASS_MAPPINGS)
|
||||
@@ -11,6 +11,7 @@ NODE_CLASS_MAPPINGS.update(float_nodes.NODE_CLASS_MAPPINGS)
|
||||
NODE_CLASS_MAPPINGS.update(int_nodes.NODE_CLASS_MAPPINGS)
|
||||
NODE_CLASS_MAPPINGS.update(list_nodes.NODE_CLASS_MAPPINGS)
|
||||
NODE_CLASS_MAPPINGS.update(set_nodes.NODE_CLASS_MAPPINGS)
|
||||
NODE_CLASS_MAPPINGS.update(math_nodes.NODE_CLASS_MAPPINGS)
|
||||
NODE_CLASS_MAPPINGS.update(string_nodes.NODE_CLASS_MAPPINGS)
|
||||
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {}
|
||||
@@ -23,4 +24,5 @@ NODE_DISPLAY_NAME_MAPPINGS.update(float_nodes.NODE_DISPLAY_NAME_MAPPINGS)
|
||||
NODE_DISPLAY_NAME_MAPPINGS.update(int_nodes.NODE_DISPLAY_NAME_MAPPINGS)
|
||||
NODE_DISPLAY_NAME_MAPPINGS.update(list_nodes.NODE_DISPLAY_NAME_MAPPINGS)
|
||||
NODE_DISPLAY_NAME_MAPPINGS.update(set_nodes.NODE_DISPLAY_NAME_MAPPINGS)
|
||||
NODE_DISPLAY_NAME_MAPPINGS.update(math_nodes.NODE_DISPLAY_NAME_MAPPINGS)
|
||||
NODE_DISPLAY_NAME_MAPPINGS.update(string_nodes.NODE_DISPLAY_NAME_MAPPINGS)
|
||||
|
||||
@@ -21,7 +21,7 @@ class IfElse:
|
||||
|
||||
RETURN_TYPES = ("*",)
|
||||
RETURN_NAMES = ("result",)
|
||||
CATEGORY = "Basic/ ⎇ Flow Control"
|
||||
CATEGORY = "Basic/Flow Control"
|
||||
DESCRIPTION = cleandoc(__doc__ or "")
|
||||
FUNCTION = "execute"
|
||||
|
||||
@@ -40,7 +40,6 @@ class IfElse:
|
||||
return needed
|
||||
|
||||
def execute(self, condition: bool, if_true: Any, if_false: Any) -> tuple[Any]:
|
||||
"""Execute the if/else statement and return the appropriate value."""
|
||||
return (if_true if condition else if_false,)
|
||||
|
||||
|
||||
@@ -73,7 +72,7 @@ class SwitchCase:
|
||||
|
||||
RETURN_TYPES = ("*",)
|
||||
RETURN_NAMES = ("result",)
|
||||
CATEGORY = "Basic/ ⎇ Flow Control"
|
||||
CATEGORY = "Basic/Flow Control"
|
||||
DESCRIPTION = cleandoc(__doc__ or "")
|
||||
FUNCTION = "execute"
|
||||
|
||||
@@ -103,7 +102,6 @@ class SwitchCase:
|
||||
def execute(self, selector: int, case_0: Any, case_1: Any,
|
||||
case_2: Any = None, case_3: Any = None,
|
||||
case_4: Any = None, default: Any = None) -> tuple[Any]:
|
||||
"""Execute the switch/case statement and return the appropriate value."""
|
||||
cases = [case_0, case_1, case_2, case_3, case_4]
|
||||
|
||||
if 0 <= selector < len(cases) and cases[selector] is not None:
|
||||
|
||||
@@ -721,6 +721,90 @@ class DataListToList:
|
||||
return (kwargs.get('list', []).copy(),)
|
||||
|
||||
|
||||
class DataListMin:
|
||||
"""
|
||||
Finds the minimum value in a list of numbers.
|
||||
|
||||
This node takes a list of numbers (either FLOAT or INT) and returns
|
||||
the minimum value. Returns None if the list is empty or if it contains
|
||||
non-numeric values.
|
||||
"""
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"list": (["FLOAT", "INT"], {}),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("*",)
|
||||
RETURN_NAMES = ("min_value",)
|
||||
CATEGORY = "Basic/data list"
|
||||
DESCRIPTION = cleandoc(__doc__ or "")
|
||||
FUNCTION = "find_min"
|
||||
INPUT_IS_LIST = True
|
||||
|
||||
@classmethod
|
||||
def VALIDATE_INPUTS(cls, input_types: dict[str, str]) -> bool|str:
|
||||
if input_types[0]["list"] not in ("FLOAT", "INT"):
|
||||
return "list must contain FLOAT or INT types"
|
||||
return True
|
||||
|
||||
def find_min(self, **kwargs: dict[str, list]) -> tuple[Any]:
|
||||
values = kwargs.get('list', [])
|
||||
if not values:
|
||||
return (None,)
|
||||
|
||||
try:
|
||||
# Return the same type as found in the minimum value
|
||||
return (min(values),)
|
||||
except (TypeError, ValueError):
|
||||
# Handle case where list contains non-comparable elements
|
||||
return (None,)
|
||||
|
||||
|
||||
class DataListMax:
|
||||
"""
|
||||
Finds the maximum value in a list of numbers.
|
||||
|
||||
This node takes a list of numbers (either FLOAT or INT) and returns
|
||||
the maximum value. Returns None if the list is empty or if it contains
|
||||
non-numeric values.
|
||||
"""
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"list": (["FLOAT", "INT"], {}),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("*",)
|
||||
RETURN_NAMES = ("max_value",)
|
||||
CATEGORY = "Basic/data list"
|
||||
DESCRIPTION = cleandoc(__doc__ or "")
|
||||
FUNCTION = "find_max"
|
||||
INPUT_IS_LIST = True
|
||||
|
||||
@classmethod
|
||||
def VALIDATE_INPUTS(cls, input_types: dict[str, str]) -> bool|str:
|
||||
if input_types[0]["list"] not in ("FLOAT", "INT"):
|
||||
return "list must contain FLOAT or INT types"
|
||||
return True
|
||||
|
||||
def find_max(self, **kwargs: dict[str, list]) -> tuple[Any]:
|
||||
values = kwargs.get('list', [])
|
||||
if not values:
|
||||
return (None,)
|
||||
|
||||
try:
|
||||
# Return the same type as found in the maximum value
|
||||
return (max(values),)
|
||||
except (TypeError, ValueError):
|
||||
# Handle case where list contains non-comparable elements
|
||||
return (None,)
|
||||
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
"DataListAppend": DataListAppend,
|
||||
"DataListExtend": DataListExtend,
|
||||
@@ -742,6 +826,8 @@ NODE_CLASS_MAPPINGS = {
|
||||
"DataListZip": DataListZip,
|
||||
"DataListFilter": DataListFilter,
|
||||
"DataListToList": DataListToList,
|
||||
"DataListMin": DataListMin,
|
||||
"DataListMax": DataListMax,
|
||||
}
|
||||
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
@@ -765,4 +851,6 @@ NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"DataListZip": "zip",
|
||||
"DataListFilter": "filter",
|
||||
"DataListToList": "convert to LIST",
|
||||
"DataListMin": "min",
|
||||
"DataListMax": "max",
|
||||
}
|
||||
|
||||
@@ -515,6 +515,70 @@ class AnyToList:
|
||||
return ([input],)
|
||||
|
||||
|
||||
class ListMin:
|
||||
"""
|
||||
Returns the minimum value in a LIST.
|
||||
|
||||
This node takes a LIST of comparable items and returns the minimum value.
|
||||
Returns None if the LIST is empty or if items are not comparable.
|
||||
"""
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"list": ("LIST", {}),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("*",)
|
||||
RETURN_NAMES = ("min_value",)
|
||||
CATEGORY = "Basic/LIST"
|
||||
DESCRIPTION = cleandoc(__doc__ or "")
|
||||
FUNCTION = "find_min"
|
||||
|
||||
def find_min(self, list: list[Any]) -> tuple[Any]:
|
||||
if not list:
|
||||
return (None,)
|
||||
|
||||
try:
|
||||
return (min(list),)
|
||||
except (TypeError, ValueError):
|
||||
# Handle case where list contains non-comparable elements
|
||||
return (None,)
|
||||
|
||||
|
||||
class ListMax:
|
||||
"""
|
||||
Returns the maximum value in a LIST.
|
||||
|
||||
This node takes a LIST of comparable items and returns the maximum value.
|
||||
Returns None if the LIST is empty or if items are not comparable.
|
||||
"""
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"list": ("LIST", {}),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("*",)
|
||||
RETURN_NAMES = ("max_value",)
|
||||
CATEGORY = "Basic/LIST"
|
||||
DESCRIPTION = cleandoc(__doc__ or "")
|
||||
FUNCTION = "find_max"
|
||||
|
||||
def find_max(self, list: list[Any]) -> tuple[Any]:
|
||||
if not list:
|
||||
return (None,)
|
||||
|
||||
try:
|
||||
return (max(list),)
|
||||
except (TypeError, ValueError):
|
||||
# Handle case where list contains non-comparable elements
|
||||
return (None,)
|
||||
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
"ListAppend": ListAppend,
|
||||
"ListExtend": ListExtend,
|
||||
@@ -534,6 +598,8 @@ NODE_CLASS_MAPPINGS = {
|
||||
"ListToDataList": ListToDataList,
|
||||
"ListToSet": ListToSet,
|
||||
"AnyToList": AnyToList,
|
||||
"ListMin": ListMin,
|
||||
"ListMax": ListMax,
|
||||
}
|
||||
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
@@ -555,4 +621,6 @@ NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"ListToDataList": "convert to data list",
|
||||
"ListToSet": "convert to SET",
|
||||
"AnyToList": "any to LIST",
|
||||
"ListMin": "min",
|
||||
"ListMax": "max",
|
||||
}
|
||||
|
||||
@@ -0,0 +1,578 @@
|
||||
import math
|
||||
from inspect import cleandoc
|
||||
from typing import Literal, Union
|
||||
|
||||
|
||||
class MathSin:
|
||||
"""
|
||||
Calculates the sine of an angle in radians or degrees.
|
||||
|
||||
This node takes an angle value and unit type, and returns the sine of that angle.
|
||||
The sine of an angle is the ratio of the length of the opposite side to the length
|
||||
of the hypotenuse in a right-angled triangle.
|
||||
"""
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"angle": ("FLOAT", {"default": 0.0}),
|
||||
"unit": (["radians", "degrees"], {"default": "radians"}),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("FLOAT",)
|
||||
CATEGORY = "Basic/maths"
|
||||
DESCRIPTION = cleandoc(__doc__ or "")
|
||||
FUNCTION = "calculate"
|
||||
|
||||
def calculate(self, angle: float, unit: Literal["radians", "degrees"]) -> tuple[float]:
|
||||
if unit == "degrees":
|
||||
# Convert degrees to radians
|
||||
angle = math.radians(angle)
|
||||
return (math.sin(angle),)
|
||||
|
||||
|
||||
class MathCos:
|
||||
"""
|
||||
Calculates the cosine of an angle in radians or degrees.
|
||||
|
||||
This node takes an angle value and unit type, and returns the cosine of that angle.
|
||||
The cosine of an angle is the ratio of the length of the adjacent side to the length
|
||||
of the hypotenuse in a right-angled triangle.
|
||||
"""
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"angle": ("FLOAT", {"default": 0.0}),
|
||||
"unit": (["radians", "degrees"], {"default": "radians"}),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("FLOAT",)
|
||||
CATEGORY = "Basic/maths"
|
||||
DESCRIPTION = cleandoc(__doc__ or "")
|
||||
FUNCTION = "calculate"
|
||||
|
||||
def calculate(self, angle: float, unit: Literal["radians", "degrees"]) -> tuple[float]:
|
||||
if unit == "degrees":
|
||||
# Convert degrees to radians
|
||||
angle = math.radians(angle)
|
||||
return (math.cos(angle),)
|
||||
|
||||
|
||||
class MathTan:
|
||||
"""
|
||||
Calculates the tangent of an angle in radians or degrees.
|
||||
|
||||
This node takes an angle value and unit type, and returns the tangent of that angle.
|
||||
The tangent of an angle is the ratio of the length of the opposite side to the length
|
||||
of the adjacent side in a right-angled triangle.
|
||||
"""
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"angle": ("FLOAT", {"default": 0.0}),
|
||||
"unit": (["radians", "degrees"], {"default": "radians"}),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("FLOAT",)
|
||||
CATEGORY = "Basic/maths"
|
||||
DESCRIPTION = cleandoc(__doc__ or "")
|
||||
FUNCTION = "calculate"
|
||||
|
||||
def calculate(self, angle: float, unit: Literal["radians", "degrees"]) -> tuple[float]:
|
||||
if unit == "degrees":
|
||||
# Convert degrees to radians
|
||||
angle = math.radians(angle)
|
||||
|
||||
# Handle specific angles that would result in division by zero
|
||||
if abs(math.cos(angle)) < 1e-10:
|
||||
raise ValueError("Tangent is undefined at this angle (division by zero)")
|
||||
|
||||
return (math.tan(angle),)
|
||||
|
||||
|
||||
class MathAsin:
|
||||
"""
|
||||
Calculates the arc sine (inverse sine) of a value.
|
||||
|
||||
This node takes a value between -1 and 1, and returns the arc sine in radians or degrees.
|
||||
The arc sine is the inverse operation of sine, returning the angle whose sine is the input value.
|
||||
"""
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"value": ("FLOAT", {"default": 0.0, "min": -1.0, "max": 1.0}),
|
||||
"unit": (["radians", "degrees"], {"default": "radians"}),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("FLOAT",)
|
||||
CATEGORY = "Basic/maths"
|
||||
DESCRIPTION = cleandoc(__doc__ or "")
|
||||
FUNCTION = "calculate"
|
||||
|
||||
def calculate(self, value: float, unit: Literal["radians", "degrees"]) -> tuple[float]:
|
||||
result = math.asin(value)
|
||||
if unit == "degrees":
|
||||
result = math.degrees(result)
|
||||
return (result,)
|
||||
|
||||
|
||||
class MathAcos:
|
||||
"""
|
||||
Calculates the arc cosine (inverse cosine) of a value.
|
||||
|
||||
This node takes a value between -1 and 1, and returns the arc cosine in radians or degrees.
|
||||
The arc cosine is the inverse operation of cosine, returning the angle whose cosine is the input value.
|
||||
"""
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"value": ("FLOAT", {"default": 1.0, "min": -1.0, "max": 1.0}),
|
||||
"unit": (["radians", "degrees"], {"default": "radians"}),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("FLOAT",)
|
||||
CATEGORY = "Basic/maths"
|
||||
DESCRIPTION = cleandoc(__doc__ or "")
|
||||
FUNCTION = "calculate"
|
||||
|
||||
def calculate(self, value: float, unit: Literal["radians", "degrees"]) -> tuple[float]:
|
||||
result = math.acos(value)
|
||||
if unit == "degrees":
|
||||
result = math.degrees(result)
|
||||
return (result,)
|
||||
|
||||
|
||||
class MathAtan:
|
||||
"""
|
||||
Calculates the arc tangent (inverse tangent) of a value.
|
||||
|
||||
This node takes a value and returns the arc tangent in radians or degrees.
|
||||
The arc tangent is the inverse operation of tangent, returning the angle whose tangent is the input value.
|
||||
"""
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"value": ("FLOAT", {"default": 0.0}),
|
||||
"unit": (["radians", "degrees"], {"default": "radians"}),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("FLOAT",)
|
||||
CATEGORY = "Basic/maths"
|
||||
DESCRIPTION = cleandoc(__doc__ or "")
|
||||
FUNCTION = "calculate"
|
||||
|
||||
def calculate(self, value: float, unit: Literal["radians", "degrees"]) -> tuple[float]:
|
||||
result = math.atan(value)
|
||||
if unit == "degrees":
|
||||
result = math.degrees(result)
|
||||
return (result,)
|
||||
|
||||
|
||||
class MathAtan2:
|
||||
"""
|
||||
Calculates the arc tangent of y/x, considering the quadrant.
|
||||
|
||||
This node takes y and x coordinates and returns the arc tangent in radians or degrees.
|
||||
Unlike atan(y/x), this function handles the case where x is zero and correctly determines
|
||||
the quadrant of the resulting angle.
|
||||
"""
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"y": ("FLOAT", {"default": 0.0}),
|
||||
"x": ("FLOAT", {"default": 1.0}),
|
||||
"unit": (["radians", "degrees"], {"default": "radians"}),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("FLOAT",)
|
||||
CATEGORY = "Basic/maths"
|
||||
DESCRIPTION = cleandoc(__doc__ or "")
|
||||
FUNCTION = "calculate"
|
||||
|
||||
def calculate(self, y: float, x: float, unit: Literal["radians", "degrees"]) -> tuple[float]:
|
||||
result = math.atan2(y, x)
|
||||
if unit == "degrees":
|
||||
result = math.degrees(result)
|
||||
return (result,)
|
||||
|
||||
|
||||
class MathSqrt:
|
||||
"""
|
||||
Calculates the square root of a non-negative number.
|
||||
|
||||
This node takes a non-negative number and returns its square root.
|
||||
"""
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"value": ("FLOAT", {"default": 0.0, "min": 0.0}),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("FLOAT",)
|
||||
CATEGORY = "Basic/maths"
|
||||
DESCRIPTION = cleandoc(__doc__ or "")
|
||||
FUNCTION = "calculate"
|
||||
|
||||
def calculate(self, value: float) -> tuple[float]:
|
||||
return (math.sqrt(value),)
|
||||
|
||||
|
||||
class MathExp:
|
||||
"""
|
||||
Calculates the exponential of a number (e^x).
|
||||
|
||||
This node takes a number and returns e (Euler's number) raised to the power of that number.
|
||||
"""
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"value": ("FLOAT", {"default": 0.0}),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("FLOAT",)
|
||||
CATEGORY = "Basic/maths"
|
||||
DESCRIPTION = cleandoc(__doc__ or "")
|
||||
FUNCTION = "calculate"
|
||||
|
||||
def calculate(self, value: float) -> tuple[float]:
|
||||
return (math.exp(value),)
|
||||
|
||||
|
||||
class MathLog:
|
||||
"""
|
||||
Calculates the natural logarithm (base e) of a number.
|
||||
|
||||
This node takes a positive number and returns its natural logarithm.
|
||||
If the specified base is not 'e', calculates the logarithm with the specified base.
|
||||
"""
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"value": ("FLOAT", {"default": 1.0, "min": 0.0000001}),
|
||||
},
|
||||
"optional": {
|
||||
"base": ("FLOAT", {"default": math.e, "min": 0.0000001}),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("FLOAT",)
|
||||
CATEGORY = "Basic/maths"
|
||||
DESCRIPTION = cleandoc(__doc__ or "")
|
||||
FUNCTION = "calculate"
|
||||
|
||||
def calculate(self, value: float, base: float = math.e) -> tuple[float]:
|
||||
if base == math.e:
|
||||
return (math.log(value),)
|
||||
else:
|
||||
return (math.log(value, base),)
|
||||
|
||||
|
||||
class MathLog10:
|
||||
"""
|
||||
Calculates the base-10 logarithm of a number.
|
||||
|
||||
This node takes a positive number and returns its base-10 logarithm.
|
||||
"""
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"value": ("FLOAT", {"default": 1.0, "min": 0.0000001}),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("FLOAT",)
|
||||
CATEGORY = "Basic/maths"
|
||||
DESCRIPTION = cleandoc(__doc__ or "")
|
||||
FUNCTION = "calculate"
|
||||
|
||||
def calculate(self, value: float) -> tuple[float]:
|
||||
return (math.log10(value),)
|
||||
|
||||
|
||||
class MathDegrees:
|
||||
"""
|
||||
Converts an angle from radians to degrees.
|
||||
|
||||
This node takes an angle in radians and returns the equivalent angle in degrees.
|
||||
"""
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"radians": ("FLOAT", {"default": 0.0}),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("FLOAT",)
|
||||
CATEGORY = "Basic/maths"
|
||||
DESCRIPTION = cleandoc(__doc__ or "")
|
||||
FUNCTION = "calculate"
|
||||
|
||||
def calculate(self, radians: float) -> tuple[float]:
|
||||
return (math.degrees(radians),)
|
||||
|
||||
|
||||
class MathRadians:
|
||||
"""
|
||||
Converts an angle from degrees to radians.
|
||||
|
||||
This node takes an angle in degrees and returns the equivalent angle in radians.
|
||||
"""
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"degrees": ("FLOAT", {"default": 0.0}),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("FLOAT",)
|
||||
CATEGORY = "Basic/maths"
|
||||
DESCRIPTION = cleandoc(__doc__ or "")
|
||||
FUNCTION = "calculate"
|
||||
|
||||
def calculate(self, degrees: float) -> tuple[float]:
|
||||
return (math.radians(degrees),)
|
||||
|
||||
|
||||
class MathFloor:
|
||||
"""
|
||||
Returns the floor of a number.
|
||||
|
||||
This node takes a number and returns the largest integer less than or equal to the input value.
|
||||
"""
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"value": ("FLOAT", {"default": 0.0}),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("INT",)
|
||||
CATEGORY = "Basic/maths"
|
||||
DESCRIPTION = cleandoc(__doc__ or "")
|
||||
FUNCTION = "calculate"
|
||||
|
||||
def calculate(self, value: float) -> tuple[int]:
|
||||
return (math.floor(value),)
|
||||
|
||||
|
||||
class MathCeil:
|
||||
"""
|
||||
Returns the ceiling of a number.
|
||||
|
||||
This node takes a number and returns the smallest integer greater than or equal to the input value.
|
||||
"""
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"value": ("FLOAT", {"default": 0.0}),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("INT",)
|
||||
CATEGORY = "Basic/maths"
|
||||
DESCRIPTION = cleandoc(__doc__ or "")
|
||||
FUNCTION = "calculate"
|
||||
|
||||
def calculate(self, value: float) -> tuple[int]:
|
||||
return (math.ceil(value),)
|
||||
|
||||
|
||||
class MathAbs:
|
||||
"""
|
||||
Returns the absolute value of a number.
|
||||
|
||||
This node takes a number and returns its absolute value (magnitude without sign).
|
||||
"""
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"value": (["FLOAT", "INT"], {}),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("*",)
|
||||
CATEGORY = "Basic/maths"
|
||||
DESCRIPTION = cleandoc(__doc__ or "")
|
||||
FUNCTION = "calculate"
|
||||
|
||||
@classmethod
|
||||
def VALIDATE_INPUTS(cls, input_types: dict[str, str]) -> bool|str:
|
||||
if input_types["value"] not in ("FLOAT", "INT"):
|
||||
return "value must be a FLOAT or INT type"
|
||||
return True
|
||||
|
||||
def calculate(self, value: Union[float, int]) -> tuple[Union[float, int]]:
|
||||
return (abs(value),)
|
||||
|
||||
|
||||
class MathPi:
|
||||
"""
|
||||
Returns the mathematical constant π (pi).
|
||||
|
||||
This node returns the value of π, which is approximately 3.14159.
|
||||
"""
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {"required": {}}
|
||||
|
||||
RETURN_TYPES = ("FLOAT",)
|
||||
CATEGORY = "Basic/maths"
|
||||
DESCRIPTION = cleandoc(__doc__ or "")
|
||||
FUNCTION = "calculate"
|
||||
|
||||
def calculate(self) -> tuple[float]:
|
||||
return (math.pi,)
|
||||
|
||||
|
||||
class MathE:
|
||||
"""
|
||||
Returns the mathematical constant e.
|
||||
|
||||
This node returns the value of e (Euler's number), which is approximately 2.71828.
|
||||
"""
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {"required": {}}
|
||||
|
||||
RETURN_TYPES = ("FLOAT",)
|
||||
CATEGORY = "Basic/maths"
|
||||
DESCRIPTION = cleandoc(__doc__ or "")
|
||||
FUNCTION = "calculate"
|
||||
|
||||
def calculate(self) -> tuple[float]:
|
||||
return (math.e,)
|
||||
|
||||
|
||||
class MathMin:
|
||||
"""
|
||||
Returns the minimum value among the inputs.
|
||||
|
||||
This node takes two or more values and returns the minimum value among them.
|
||||
The function works with both FLOAT and INT types.
|
||||
"""
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"value1": (["FLOAT", "INT"], {"default": 0}),
|
||||
"value2": (["FLOAT", "INT"], {"default": 0}),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("*",)
|
||||
CATEGORY = "Basic/maths"
|
||||
DESCRIPTION = cleandoc(__doc__ or "")
|
||||
FUNCTION = "calculate"
|
||||
|
||||
@classmethod
|
||||
def VALIDATE_INPUTS(cls, input_types: dict[str, str]) -> bool|str:
|
||||
for key, value in input_types.items():
|
||||
if key.startswith("value") and value not in ("FLOAT", "INT"):
|
||||
return f"{key} must be a FLOAT or INT type"
|
||||
return True
|
||||
|
||||
def calculate(self, value1: Union[float, int], value2: Union[float, int]) -> tuple[Union[float, int]]:
|
||||
return (min(value1, value2),)
|
||||
|
||||
|
||||
class MathMax:
|
||||
"""
|
||||
Returns the maximum value among the inputs.
|
||||
|
||||
This node takes two or more values and returns the maximum value among them.
|
||||
The function works with both FLOAT and INT types.
|
||||
"""
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"value1": (["FLOAT", "INT"], {"default": 0}),
|
||||
"value2": (["FLOAT", "INT"], {"default": 0}),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("*",)
|
||||
CATEGORY = "Basic/maths"
|
||||
DESCRIPTION = cleandoc(__doc__ or "")
|
||||
FUNCTION = "calculate"
|
||||
|
||||
@classmethod
|
||||
def VALIDATE_INPUTS(cls, input_types: dict[str, str]) -> bool|str:
|
||||
for key, value in input_types.items():
|
||||
if key.startswith("value") and value not in ("FLOAT", "INT"):
|
||||
return f"{key} must be a FLOAT or INT type"
|
||||
return True
|
||||
|
||||
def calculate(self, value1: Union[float, int], value2: Union[float, int]) -> tuple[Union[float, int]]:
|
||||
return (max(value1, value2),)
|
||||
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
"MathSin": MathSin,
|
||||
"MathCos": MathCos,
|
||||
"MathTan": MathTan,
|
||||
"MathAsin": MathAsin,
|
||||
"MathAcos": MathAcos,
|
||||
"MathAtan": MathAtan,
|
||||
"MathAtan2": MathAtan2,
|
||||
"MathSqrt": MathSqrt,
|
||||
"MathExp": MathExp,
|
||||
"MathLog": MathLog,
|
||||
"MathLog10": MathLog10,
|
||||
"MathDegrees": MathDegrees,
|
||||
"MathRadians": MathRadians,
|
||||
"MathFloor": MathFloor,
|
||||
"MathCeil": MathCeil,
|
||||
"MathAbs": MathAbs,
|
||||
"MathPi": MathPi,
|
||||
"MathE": MathE,
|
||||
"MathMin": MathMin,
|
||||
"MathMax": MathMax,
|
||||
}
|
||||
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"MathSin": "sin",
|
||||
"MathCos": "cos",
|
||||
"MathTan": "tan",
|
||||
"MathAsin": "asin",
|
||||
"MathAcos": "acos",
|
||||
"MathAtan": "atan",
|
||||
"MathAtan2": "atan2",
|
||||
"MathSqrt": "sqrt",
|
||||
"MathExp": "exp",
|
||||
"MathLog": "log",
|
||||
"MathLog10": "log10",
|
||||
"MathDegrees": "degrees",
|
||||
"MathRadians": "radians",
|
||||
"MathFloor": "floor",
|
||||
"MathCeil": "ceil",
|
||||
"MathAbs": "abs",
|
||||
"MathPi": "pi",
|
||||
"MathE": "e",
|
||||
"MathMin": "min",
|
||||
"MathMax": "max",
|
||||
}
|
||||
Reference in New Issue
Block a user