108 lines
3.9 KiB
Python
108 lines
3.9 KiB
Python
import random
|
|
import time
|
|
|
|
class DynamicValueRange:
|
|
"""
|
|
Automatically cycles through a range of values on each run.
|
|
Use to create diversity in settings across multiple generations.
|
|
"""
|
|
|
|
# Class variable to track current values per node instance
|
|
_state = {}
|
|
|
|
@classmethod
|
|
def INPUT_TYPES(cls):
|
|
return {
|
|
"required": {
|
|
"min_value": ("FLOAT", {"default": 0.0, "min": -10000.0, "max": 10000.0, "step": 0.1}),
|
|
"max_value": ("FLOAT", {"default": 1.0, "min": -10000.0, "max": 10000.0, "step": 0.1}),
|
|
"step": ("FLOAT", {"default": 0.1, "min": 0.001, "max": 1000.0, "step": 0.01}),
|
|
"mode": (["increment", "decrement", "random"], {"default": "increment"}),
|
|
"on_cycle_complete": (["reverse", "jump"], {"default": "reverse"}),
|
|
"output_int": ("BOOLEAN", {"default": False}),
|
|
},
|
|
"hidden": {
|
|
"unique_id": "UNIQUE_ID",
|
|
},
|
|
}
|
|
|
|
RETURN_TYPES = ("FLOAT", "INT", "STRING")
|
|
RETURN_NAMES = ("value", "value_int", "value_str")
|
|
FUNCTION = "get_value"
|
|
CATEGORY = "MachinePaintingNodes/Util"
|
|
|
|
@classmethod
|
|
def IS_CHANGED(cls, **kwargs):
|
|
# Always return a different value to force re-execution
|
|
return float("nan")
|
|
|
|
def get_value(self, min_value, max_value, step, mode, on_cycle_complete, output_int, unique_id=None):
|
|
# Use unique_id to track state per node instance
|
|
node_id = str(unique_id) if unique_id else "default"
|
|
|
|
# Initialize state if not exists
|
|
if node_id not in DynamicValueRange._state:
|
|
DynamicValueRange._state[node_id] = {
|
|
"current": min_value,
|
|
"direction": 1, # 1 = forward, -1 = backward
|
|
}
|
|
|
|
state = DynamicValueRange._state[node_id]
|
|
|
|
# Handle random mode
|
|
if mode == "random":
|
|
# Calculate number of steps
|
|
num_steps = int((max_value - min_value) / step) + 1
|
|
random_step = random.randint(0, num_steps - 1)
|
|
value = min_value + (random_step * step)
|
|
value = min(max_value, max(min_value, value))
|
|
else:
|
|
# Get current value
|
|
value = state["current"]
|
|
|
|
# Ensure value is within range (in case settings changed)
|
|
value = min(max_value, max(min_value, value))
|
|
|
|
# Calculate next value for next run
|
|
if mode == "increment":
|
|
direction = state.get("direction", 1)
|
|
else: # decrement
|
|
direction = state.get("direction", -1)
|
|
if state.get("direction") is None or state.get("direction") == 1:
|
|
state["direction"] = -1
|
|
direction = -1
|
|
|
|
next_value = value + (step * direction)
|
|
|
|
# Check if we've hit the bounds
|
|
if next_value > max_value:
|
|
if on_cycle_complete == "reverse":
|
|
state["direction"] = -1
|
|
next_value = value - step
|
|
else: # jump
|
|
next_value = min_value
|
|
elif next_value < min_value:
|
|
if on_cycle_complete == "reverse":
|
|
state["direction"] = 1
|
|
next_value = value + step
|
|
else: # jump
|
|
next_value = max_value
|
|
|
|
# Clamp next value
|
|
next_value = min(max_value, max(min_value, next_value))
|
|
|
|
# Store for next run
|
|
state["current"] = next_value
|
|
|
|
# Round to avoid floating point errors
|
|
value = round(value, 6)
|
|
|
|
# Convert outputs
|
|
value_int = int(round(value))
|
|
value_str = str(value_int if output_int else value)
|
|
|
|
if output_int:
|
|
value = float(value_int)
|
|
|
|
return (value, value_int, value_str)
|