diff --git a/__init__.py b/__init__.py index 0d04432..95aa1ed 100644 --- a/__init__.py +++ b/__init__.py @@ -4,7 +4,11 @@ from .nodes import * -NODE_CLASS_MAPPINGS = {} -NODE_DISPLAY_NAME_MAPPINGS = {} +NODE_CLASS_MAPPINGS = { + "BestResolutionSimple": BestResolutionSimple, +} +NODE_DISPLAY_NAME_MAPPINGS = { + "BestResolutionSimple": "Best-Res (simple)", +} __all__ = ["NODE_CLASS_MAPPINGS", "NODE_DISPLAY_NAME_MAPPINGS"] diff --git a/node_types.py b/node_types.py new file mode 100644 index 0000000..d8a5765 --- /dev/null +++ b/node_types.py @@ -0,0 +1,34 @@ +# encoding: utf-8 +""" +""" + +import typing as _t + +import sys as _sys + + +def number_type_dict(default: int = 512, min: int = 1, max: int = _sys.maxsize, step: int = 1) -> _t.Dict[str, int]: + """Prepare type-dict for an input int/float parameter.""" + + # sys.maxsize is Py3's closest equivalent to 'sys.maxint' from Py2 + return { + "default": default, + "min": min, + "max": max, + "step": step + } + + +def number_to_int(value: _t.Union[int, float], min: int = 1) -> int: + if not isinstance(value, int): + if isinstance(value, float): + sign = -1 if value < 0.0 else 1 + value = int(value * sign + 0.5) * sign + else: + value = int(value) + return max(value, min) + + +type_dict_step_init = number_type_dict(48) +type_dict_step_upscale1 = number_type_dict(128) +type_dict_res = number_type_dict(512) diff --git a/nodes.py b/nodes.py index 03ef0e8..2240a5a 100644 --- a/nodes.py +++ b/nodes.py @@ -3,3 +3,66 @@ """ import typing as _t + +from frozendict import deepfreeze + +from comfy.comfy_types.node_typing import IO as _IO + +from .node_types import * + + +def _round_abs(abs_value: _t.Union[int, float], step: int): + """ + Assuming both args are positive and ``step`` is already an int, detect the closest positive (non-zero) value + which is also divisible by step. + """ + n_steps = int(float(number_to_int(abs_value)) / step + 0.5) + n_steps = max(n_steps, 1) + return step * n_steps, n_steps + + +# Tiny optimization by reusing the same immutable dict: +_input_types_simple = deepfreeze({ + "required": { + "width": (_IO.INT, dict(type_dict_res, **{'tooltip': "Approximate width"})), + "height": (_IO.INT, dict(type_dict_res, **{'tooltip': "Approximate height"})), + "step": (_IO.INT, dict(type_dict_step_init, **{'tooltip': "Resolution must be divisible by this value"})), + }, + # "hidden": {}, + # "optional": {}, +}) + + +class BestResolutionSimple: + NODE_NAME = "BestResolutionSimple" + CATEGORY = "utils/resolution" + + # OUTPUT_NODE = True # TODO + + FUNCTION = "main" + RETURN_TYPES = (_IO.INT, _IO.INT) + RETURN_NAMES = ("width", "height") + + @classmethod + def INPUT_TYPES(cls): + return _input_types_simple + + def main(self, width: int, height: int, step: int): + step = number_to_int(step) + + width, n_steps_x = _round_abs(width, step) + height, n_steps_y = _round_abs(height, step) + + width = n_steps_x * step + height = n_steps_y * step + + text = f"{width} x {height}\n{n_steps_x} * {step} x {n_steps_y} * {step}" + print(f"text: {text}") + + # The following return format is snatched from built-in PreviewAny + # and easy_nodes.show_text from (https://github.com/andrewharp/ComfyUI-EasyNodes); + # doesn't seem to be documented anywhere 🤷🏻‍♂️ + return { + # "ui": {"text": [text]}, # TODO: for some reason, it isn't displayed + "result": (width, height) + } diff --git a/pyproject.toml b/pyproject.toml index dd0211e..c500b1c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -7,7 +7,7 @@ authors = [ ] license = {file = "LICENSE.md"} readme = "README.md" -dependencies = [] +dependencies = ["frozendict"] [project.urls] Repository = "https://github.com/Lex-DRL/ComfyUI-BestResolution" @@ -15,5 +15,5 @@ Repository = "https://github.com/Lex-DRL/ComfyUI-BestResolution" [tool.comfy] PublisherId = "lex-drl" -DisplayName = "ComfyUI-BestResolution" +DisplayName = "Best Resolution" Icon = "" diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..302328f --- /dev/null +++ b/requirements.txt @@ -0,0 +1 @@ +frozendict