diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..68bc17f --- /dev/null +++ b/.gitignore @@ -0,0 +1,160 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +#pdm.lock +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm.fming.dev/#use-with-ide +.pdm.toml + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# PyCharm +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +#.idea/ diff --git a/README.md b/README.md new file mode 100644 index 0000000..a5b5161 --- /dev/null +++ b/README.md @@ -0,0 +1,23 @@ +# XYPlot: Comfy plugin + + +![Image](./workflows/xy_plot_mini.png) +[Workflows](./workflows/xy_plot_mini.json) +![Image](./workflows/xy_plot_base.png) +[Workflows](./workflows/xy_plot_base.json) + + +## How to use + +### Install + +``` +cd custom_nodes # From comfy path +git clone https://github.com/LEv145/XY-plot-comfy-plugin XYPlot +``` +### Update + +``` +cd custom_nodes/XYPlot +git pull +``` diff --git a/__init__.py b/__init__.py new file mode 100644 index 0000000..6f2ba5d --- /dev/null +++ b/__init__.py @@ -0,0 +1,8 @@ +from .src import ImageSetAreaNode, FloatImageCombineNode, XYPlotNode + + +NODE_CLASS_MAPPINGS = { + "ImageSetArea": ImageSetAreaNode, + "FloatImageCombine": FloatImageCombineNode, + "XYPlot": XYPlotNode, +} diff --git a/src/__init__.py b/src/__init__.py new file mode 100644 index 0000000..5124cd7 --- /dev/null +++ b/src/__init__.py @@ -0,0 +1,3 @@ +from .nodes.image_set_area import ImageSetAreaNode +from .nodes.float_image_combine import FloatImageCombineNode +from .nodes.xy_plot import XYPlotNode diff --git a/src/base.py b/src/base.py new file mode 100644 index 0000000..2e35db6 --- /dev/null +++ b/src/base.py @@ -0,0 +1,10 @@ +import typing as t + + +class BasePlotNode(): + CATEGORY: str = "XYPlot" + FUNCTION: str = "execute" + + +Image = t.Any +FloatImage = list[Image] diff --git a/src/nodes/float_image_combine.py b/src/nodes/float_image_combine.py new file mode 100644 index 0000000..9eac417 --- /dev/null +++ b/src/nodes/float_image_combine.py @@ -0,0 +1,19 @@ +import typing as t + +from ..base import BasePlotNode, FloatImage + + +class FloatImageCombineNode(BasePlotNode): + RETURN_TYPES: t.Tuple[str] = ("FLOAT_IMAGE",) + + @classmethod + def INPUT_TYPES(cls) -> t.Dict[str, t.Any]: + return { + "required": { + "float_image_1": ("FLOAT_IMAGE",), + "float_image_2": ("FLOAT_IMAGE",), + }, + } + + def execute(self, float_image_1: FloatImage, float_image_2: FloatImage) -> tuple[FloatImage]: + return (float_image_1 + float_image_2,) diff --git a/src/nodes/image_set_area.py b/src/nodes/image_set_area.py new file mode 100644 index 0000000..acb25a4 --- /dev/null +++ b/src/nodes/image_set_area.py @@ -0,0 +1,21 @@ +import typing as t + +from ..base import BasePlotNode, FloatImage, Image + + +class ImageSetAreaNode(BasePlotNode): + RETURN_TYPES: t.Tuple[str] = ("FLOAT_IMAGE",) + + def __init__(self): + pass + + @classmethod + def INPUT_TYPES(cls) -> t.Dict[str, t.Any]: + return { + "required": { + "image": ("IMAGE",), + }, + } + + def execute(self, image: Image) -> tuple[FloatImage]: + return ([image],) diff --git a/src/nodes/xy_plot.py b/src/nodes/xy_plot.py new file mode 100644 index 0000000..ce5e722 --- /dev/null +++ b/src/nodes/xy_plot.py @@ -0,0 +1,30 @@ +import typing as t + +from ..base import BasePlotNode, FloatImage, Image +from ..utils import tensor_to_pillow, pillow_to_tensor, create_image_grid + + +class XYPlotNode(BasePlotNode): + RETURN_TYPES: t.Tuple[str] = ("IMAGE",) + + @classmethod + def INPUT_TYPES(cls) -> t.Dict[str, t.Any]: + return { + "required": { + "float_image": ("FLOAT_IMAGE",), + "gap": ("INT", {"default": 0, "min": 0}), + "nrow": ("INT", {"default": 1, "min": 1}), + }, + } + + def execute( + self, + float_image: FloatImage, + nrow: int, + gap: int + ) -> tuple[Image]: + pillow_images = [tensor_to_pillow(i) for i in float_image] + pillow_grid = create_image_grid(pillow_images, nrow=nrow, gap=gap) + tensor_grid = pillow_to_tensor(pillow_grid) + + return (tensor_grid,) diff --git a/src/utils.py b/src/utils.py new file mode 100644 index 0000000..d9a07c3 --- /dev/null +++ b/src/utils.py @@ -0,0 +1,47 @@ +import typing as t + +import torch +import numpy as np +from PIL import Image + + +def tensor_to_pillow(image): + return Image.fromarray(np.clip(255. * image.cpu().numpy().squeeze(), 0, 255).astype(np.uint8)) + + +def pillow_to_tensor(image): + return torch.from_numpy(np.array(image).astype(np.float32) / 255.0).unsqueeze(0) + + +def create_image_grid(images, gap, nrow): + """ + Create a grid of images with a specified gap and number of rows. + + Args: + images (List[PIL.Image.Image]): List of images to be placed in the grid. + gap (int, optional): Gap between images in pixels. Defaults to 10. + nrow (int, optional): Number of rows in the grid. Defaults to 3. + + Returns: + PIL.Image.Image: The merged image grid. + """ + # Calculate number of columns based on number of rows and images + ncol = (len(images) + nrow - 1) // nrow + + # Get size of each image in pixels + image_width, image_height = images[0].size + + # Create new image to hold the grid + grid_width = ncol * image_width + (ncol - 1) * gap + grid_height = nrow * image_height + (nrow - 1) * gap + grid_image = Image.new("RGB", (grid_width, grid_height), color="white") + + # Paste images into grid + for i, image in enumerate(images): + row = i // ncol + col = i % ncol + x = col * (image_width + gap) + y = row * (image_height + gap) + grid_image.paste(image, (x, y)) + + return grid_image diff --git a/workflows/xy_plot_base.json b/workflows/xy_plot_base.json new file mode 100644 index 0000000..54f5291 --- /dev/null +++ b/workflows/xy_plot_base.json @@ -0,0 +1,1136 @@ +{ + "last_node_id": 40, + "last_link_id": 72, + "nodes": [ + { + "id": 15, + "type": "PreviewImage", + "pos": [ + 1435, + 325 + ], + "size": { + "0": 363.55511474609375, + "1": 290.6986999511719 + }, + "flags": {}, + "order": 20, + "mode": 0, + "inputs": [ + { + "name": "images", + "type": "IMAGE", + "link": 20 + } + ], + "properties": { + "Node name for S&R": "PreviewImage" + } + }, + { + "id": 32, + "type": "VAEDecode", + "pos": [ + 188.6033769999996, + 306.84141700000043 + ], + "size": { + "0": 210, + "1": 46 + }, + "flags": {}, + "order": 11, + "mode": 0, + "inputs": [ + { + "name": "samples", + "type": "LATENT", + "link": 49 + }, + { + "name": "vae", + "type": "VAE", + "link": 64 + } + ], + "outputs": [ + { + "name": "IMAGE", + "type": "IMAGE", + "links": [ + 51 + ], + "slot_index": 0 + } + ], + "properties": { + "Node name for S&R": "VAEDecode" + } + }, + { + "id": 34, + "type": "VAEDecode", + "pos": [ + 190.6033769999996, + 397.8414170000003 + ], + "size": { + "0": 210, + "1": 46 + }, + "flags": {}, + "order": 12, + "mode": 0, + "inputs": [ + { + "name": "samples", + "type": "LATENT", + "link": 50 + }, + { + "name": "vae", + "type": "VAE", + "link": 65 + } + ], + "outputs": [ + { + "name": "IMAGE", + "type": "IMAGE", + "links": [ + 53 + ], + "slot_index": 0 + } + ], + "properties": { + "Node name for S&R": "VAEDecode" + } + }, + { + "id": 11, + "type": "ImageSetArea", + "pos": [ + 435, + 376 + ], + "size": { + "0": 216.59999084472656, + "1": 26 + }, + "flags": {}, + "order": 15, + "mode": 0, + "inputs": [ + { + "name": "image", + "type": "IMAGE", + "link": 53, + "slot_index": 0 + } + ], + "outputs": [ + { + "name": "FLOAT_IMAGE", + "type": "FLOAT_IMAGE", + "links": [ + 13 + ], + "slot_index": 0 + } + ], + "properties": { + "Node name for S&R": "ImageSetArea" + } + }, + { + "id": 25, + "type": "CLIPTextEncode", + "pos": [ + -728.3966230000002, + 324.8414170000003 + ], + "size": { + "0": 400, + "1": 200 + }, + "flags": {}, + "order": 3, + "mode": 0, + "inputs": [ + { + "name": "clip", + "type": "CLIP", + "link": 35 + } + ], + "outputs": [ + { + "name": "CONDITIONING", + "type": "CONDITIONING", + "links": [ + 41, + 48, + 54 + ], + "slot_index": 0 + } + ], + "properties": { + "Node name for S&R": "CLIPTextEncode" + }, + "widgets_values": [ + "masterpiece, best quality, 1girl, golden hair," + ] + }, + { + "id": 26, + "type": "CLIPTextEncode", + "pos": [ + -726.3966230000002, + 564.8414170000005 + ], + "size": { + "0": 400, + "1": 200 + }, + "flags": {}, + "order": 4, + "mode": 0, + "inputs": [ + { + "name": "clip", + "type": "CLIP", + "link": 36 + } + ], + "outputs": [ + { + "name": "CONDITIONING", + "type": "CONDITIONING", + "links": [ + 40, + 47, + 55 + ], + "slot_index": 0 + } + ], + "properties": { + "Node name for S&R": "CLIPTextEncode" + }, + "widgets_values": [ + "(worst quality, low quality:1.4), monochrome, zombie" + ] + }, + { + "id": 37, + "type": "VAEDecode", + "pos": [ + 195.60337699999963, + 489.8414170000004 + ], + "size": { + "0": 210, + "1": 46 + }, + "flags": {}, + "order": 13, + "mode": 0, + "inputs": [ + { + "name": "samples", + "type": "LATENT", + "link": 58 + }, + { + "name": "vae", + "type": "VAE", + "link": 66 + } + ], + "outputs": [ + { + "name": "IMAGE", + "type": "IMAGE", + "links": [ + 59 + ], + "slot_index": 0 + } + ], + "properties": { + "Node name for S&R": "VAEDecode" + } + }, + { + "id": 24, + "type": "CheckpointLoaderSimple", + "pos": [ + -1129.3966229999999, + 302.84141700000043 + ], + "size": { + "0": 315, + "1": 98 + }, + "flags": {}, + "order": 0, + "mode": 0, + "outputs": [ + { + "name": "MODEL", + "type": "MODEL", + "links": [ + 69 + ], + "slot_index": 0 + }, + { + "name": "CLIP", + "type": "CLIP", + "links": [ + 35, + 36 + ], + "slot_index": 1 + }, + { + "name": "VAE", + "type": "VAE", + "links": [ + 63 + ], + "slot_index": 2 + } + ], + "properties": { + "Node name for S&R": "CheckpointLoaderSimple" + }, + "widgets_values": [ + "Counterfeit-v2_5.safetensors" + ] + }, + { + "id": 28, + "type": "Reroute", + "pos": [ + -773.5473798359377, + 114.2723633500978 + ], + "size": [ + 75, + 26 + ], + "flags": {}, + "order": 2, + "mode": 0, + "inputs": [ + { + "name": "", + "type": "*", + "link": 69, + "slot_index": 0 + } + ], + "outputs": [ + { + "name": "", + "type": "MODEL", + "links": [ + 44 + ] + } + ], + "properties": { + "showOutputText": false, + "horizontal": false + } + }, + { + "id": 39, + "type": "Reroute", + "pos": [ + -770.3966230000002, + 78.84141700000006 + ], + "size": [ + 75, + 26 + ], + "flags": {}, + "order": 5, + "mode": 0, + "inputs": [ + { + "name": "", + "type": "*", + "link": 63 + } + ], + "outputs": [ + { + "name": "", + "type": "VAE", + "links": [ + 67 + ], + "slot_index": 0 + } + ], + "properties": { + "showOutputText": false, + "horizontal": false + } + }, + { + "id": 29, + "type": "Reroute", + "pos": [ + -361.39662299999907, + 113.84141700000015 + ], + "size": [ + 75, + 26 + ], + "flags": {}, + "order": 6, + "mode": 0, + "inputs": [ + { + "name": "", + "type": "*", + "link": 44, + "slot_index": 0 + } + ], + "outputs": [ + { + "name": "", + "type": "MODEL", + "links": [ + 70, + 71, + 72 + ], + "slot_index": 0 + } + ], + "properties": { + "showOutputText": false, + "horizontal": false + } + }, + { + "id": 40, + "type": "Reroute", + "pos": [ + 64.60337700000011, + 78.84141700000006 + ], + "size": [ + 75, + 26 + ], + "flags": {}, + "order": 7, + "mode": 0, + "inputs": [ + { + "name": "", + "type": "*", + "link": 67, + "slot_index": 0 + } + ], + "outputs": [ + { + "name": "", + "type": "VAE", + "links": [ + 64, + 65, + 66 + ], + "slot_index": 0 + } + ], + "properties": { + "showOutputText": false, + "horizontal": false + } + }, + { + "id": 27, + "type": "KSampler", + "pos": [ + -213.39662300000015, + 303.84141700000043 + ], + "size": { + "0": 315, + "1": 262 + }, + "flags": {}, + "order": 8, + "mode": 0, + "inputs": [ + { + "name": "model", + "type": "MODEL", + "link": 70 + }, + { + "name": "positive", + "type": "CONDITIONING", + "link": 41 + }, + { + "name": "negative", + "type": "CONDITIONING", + "link": 40 + }, + { + "name": "latent_image", + "type": "LATENT", + "link": 62 + } + ], + "outputs": [ + { + "name": "LATENT", + "type": "LATENT", + "links": [ + 49 + ], + "slot_index": 0 + } + ], + "properties": { + "Node name for S&R": "KSampler" + }, + "widgets_values": [ + 1, + false, + 20, + 8, + "dpmpp_sde", + "karras", + 1 + ] + }, + { + "id": 31, + "type": "KSampler", + "pos": [ + -216.3966230000001, + 606.8414170000007 + ], + "size": { + "0": 315, + "1": 262 + }, + "flags": {}, + "order": 9, + "mode": 0, + "inputs": [ + { + "name": "model", + "type": "MODEL", + "link": 71 + }, + { + "name": "positive", + "type": "CONDITIONING", + "link": 48 + }, + { + "name": "negative", + "type": "CONDITIONING", + "link": 47 + }, + { + "name": "latent_image", + "type": "LATENT", + "link": 61 + } + ], + "outputs": [ + { + "name": "LATENT", + "type": "LATENT", + "links": [ + 50 + ], + "slot_index": 0 + } + ], + "properties": { + "Node name for S&R": "KSampler" + }, + "widgets_values": [ + 1, + false, + 20, + 10, + "dpmpp_sde", + "karras", + 1 + ] + }, + { + "id": 38, + "type": "EmptyLatentImage", + "pos": [ + -647.3966230000001, + 808.8414170000001 + ], + "size": { + "0": 315, + "1": 106 + }, + "flags": {}, + "order": 1, + "mode": 0, + "outputs": [ + { + "name": "LATENT", + "type": "LATENT", + "links": [ + 60, + 61, + 62 + ], + "slot_index": 0 + } + ], + "properties": { + "Node name for S&R": "EmptyLatentImage" + }, + "widgets_values": [ + 512, + 512, + 1 + ] + }, + { + "id": 18, + "type": "ImageSetArea", + "pos": [ + 434, + 441 + ], + "size": { + "0": 216.59999084472656, + "1": 26 + }, + "flags": {}, + "order": 16, + "mode": 0, + "inputs": [ + { + "name": "image", + "type": "IMAGE", + "link": 59 + } + ], + "outputs": [ + { + "name": "FLOAT_IMAGE", + "type": "FLOAT_IMAGE", + "links": [ + 22 + ], + "slot_index": 0 + } + ], + "properties": { + "Node name for S&R": "ImageSetArea" + } + }, + { + "id": 10, + "type": "ImageSetArea", + "pos": [ + 437, + 307 + ], + "size": { + "0": 216.59999084472656, + "1": 26 + }, + "flags": {}, + "order": 14, + "mode": 0, + "inputs": [ + { + "name": "image", + "type": "IMAGE", + "link": 51 + } + ], + "outputs": [ + { + "name": "FLOAT_IMAGE", + "type": "FLOAT_IMAGE", + "links": [ + 12 + ], + "slot_index": 0 + } + ], + "properties": { + "Node name for S&R": "ImageSetArea" + } + }, + { + "id": 12, + "type": "FloatImageCombine", + "pos": [ + 711, + 301 + ], + "size": { + "0": 317.4000244140625, + "1": 46 + }, + "flags": {}, + "order": 17, + "mode": 0, + "inputs": [ + { + "name": "float_image_1", + "type": "FLOAT_IMAGE", + "link": 12 + }, + { + "name": "float_image_2", + "type": "FLOAT_IMAGE", + "link": 13 + } + ], + "outputs": [ + { + "name": "FLOAT_IMAGE", + "type": "FLOAT_IMAGE", + "links": [ + 32 + ], + "slot_index": 0 + } + ], + "properties": { + "Node name for S&R": "FloatImageCombine" + } + }, + { + "id": 19, + "type": "FloatImageCombine", + "pos": [ + 712, + 387 + ], + "size": { + "0": 317.4000244140625, + "1": 46 + }, + "flags": {}, + "order": 18, + "mode": 0, + "inputs": [ + { + "name": "float_image_1", + "type": "FLOAT_IMAGE", + "link": 32 + }, + { + "name": "float_image_2", + "type": "FLOAT_IMAGE", + "link": 22 + } + ], + "outputs": [ + { + "name": "FLOAT_IMAGE", + "type": "FLOAT_IMAGE", + "links": [ + 24 + ], + "slot_index": 0 + } + ], + "properties": { + "Node name for S&R": "FloatImageCombine" + } + }, + { + "id": 17, + "type": "XYPlot", + "pos": [ + 1075, + 326 + ], + "size": { + "0": 315, + "1": 82 + }, + "flags": {}, + "order": 19, + "mode": 0, + "inputs": [ + { + "name": "float_image", + "type": "FLOAT_IMAGE", + "link": 24 + } + ], + "outputs": [ + { + "name": "IMAGE", + "type": "IMAGE", + "links": [ + 20 + ], + "slot_index": 0 + } + ], + "properties": { + "Node name for S&R": "XYPlot" + }, + "widgets_values": [ + 10, + 1 + ] + }, + { + "id": 36, + "type": "KSampler", + "pos": [ + -214, + 915 + ], + "size": { + "0": 315, + "1": 262 + }, + "flags": {}, + "order": 10, + "mode": 0, + "inputs": [ + { + "name": "model", + "type": "MODEL", + "link": 72 + }, + { + "name": "positive", + "type": "CONDITIONING", + "link": 54 + }, + { + "name": "negative", + "type": "CONDITIONING", + "link": 55 + }, + { + "name": "latent_image", + "type": "LATENT", + "link": 60 + } + ], + "outputs": [ + { + "name": "LATENT", + "type": "LATENT", + "links": [ + 58 + ], + "slot_index": 0 + } + ], + "properties": { + "Node name for S&R": "KSampler" + }, + "widgets_values": [ + 1, + false, + 20, + 12, + "dpmpp_sde", + "karras", + 1 + ] + } + ], + "links": [ + [ + 12, + 10, + 0, + 12, + 0, + "FLOAT_IMAGE" + ], + [ + 13, + 11, + 0, + 12, + 1, + "FLOAT_IMAGE" + ], + [ + 20, + 17, + 0, + 15, + 0, + "IMAGE" + ], + [ + 22, + 18, + 0, + 19, + 1, + "FLOAT_IMAGE" + ], + [ + 24, + 19, + 0, + 17, + 0, + "FLOAT_IMAGE" + ], + [ + 32, + 12, + 0, + 19, + 0, + "FLOAT_IMAGE" + ], + [ + 35, + 24, + 1, + 25, + 0, + "CLIP" + ], + [ + 36, + 24, + 1, + 26, + 0, + "CLIP" + ], + [ + 40, + 26, + 0, + 27, + 2, + "CONDITIONING" + ], + [ + 41, + 25, + 0, + 27, + 1, + "CONDITIONING" + ], + [ + 44, + 28, + 0, + 29, + 0, + "*" + ], + [ + 47, + 26, + 0, + 31, + 2, + "CONDITIONING" + ], + [ + 48, + 25, + 0, + 31, + 1, + "CONDITIONING" + ], + [ + 49, + 27, + 0, + 32, + 0, + "LATENT" + ], + [ + 50, + 31, + 0, + 34, + 0, + "LATENT" + ], + [ + 51, + 32, + 0, + 10, + 0, + "IMAGE" + ], + [ + 53, + 34, + 0, + 11, + 0, + "IMAGE" + ], + [ + 54, + 25, + 0, + 36, + 1, + "CONDITIONING" + ], + [ + 55, + 26, + 0, + 36, + 2, + "CONDITIONING" + ], + [ + 58, + 36, + 0, + 37, + 0, + "LATENT" + ], + [ + 59, + 37, + 0, + 18, + 0, + "IMAGE" + ], + [ + 60, + 38, + 0, + 36, + 3, + "LATENT" + ], + [ + 61, + 38, + 0, + 31, + 3, + "LATENT" + ], + [ + 62, + 38, + 0, + 27, + 3, + "LATENT" + ], + [ + 63, + 24, + 2, + 39, + 0, + "*" + ], + [ + 64, + 40, + 0, + 32, + 1, + "VAE" + ], + [ + 65, + 40, + 0, + 34, + 1, + "VAE" + ], + [ + 66, + 40, + 0, + 37, + 1, + "VAE" + ], + [ + 67, + 39, + 0, + 40, + 0, + "*" + ], + [ + 69, + 24, + 0, + 28, + 0, + "*" + ], + [ + 70, + 29, + 0, + 27, + 0, + "MODEL" + ], + [ + 71, + 29, + 0, + 31, + 0, + "MODEL" + ], + [ + 72, + 29, + 0, + 36, + 0, + "MODEL" + ] + ], + "groups": [], + "config": {}, + "extra": {}, + "version": 0.4 +} \ No newline at end of file diff --git a/workflows/xy_plot_base.png b/workflows/xy_plot_base.png new file mode 100644 index 0000000..efc6398 Binary files /dev/null and b/workflows/xy_plot_base.png differ diff --git a/workflows/xy_plot_mini.json b/workflows/xy_plot_mini.json new file mode 100644 index 0000000..406dbd0 --- /dev/null +++ b/workflows/xy_plot_mini.json @@ -0,0 +1,445 @@ +{ + "last_node_id": 44, + "last_link_id": 75, + "nodes": [ + { + "id": 15, + "type": "PreviewImage", + "pos": [ + 1435, + 325 + ], + "size": { + "0": 363.55511474609375, + "1": 290.6986999511719 + }, + "flags": {}, + "order": 9, + "mode": 0, + "inputs": [ + { + "name": "images", + "type": "IMAGE", + "link": 20 + } + ], + "properties": { + "Node name for S&R": "PreviewImage" + } + }, + { + "id": 10, + "type": "ImageSetArea", + "pos": [ + 437, + 307 + ], + "size": { + "0": 216.59999084472656, + "1": 26 + }, + "flags": {}, + "order": 3, + "mode": 0, + "inputs": [ + { + "name": "image", + "type": "IMAGE", + "link": 73 + } + ], + "outputs": [ + { + "name": "FLOAT_IMAGE", + "type": "FLOAT_IMAGE", + "links": [ + 12 + ], + "slot_index": 0 + } + ], + "properties": { + "Node name for S&R": "ImageSetArea" + } + }, + { + "id": 12, + "type": "FloatImageCombine", + "pos": [ + 711, + 301 + ], + "size": { + "0": 317.4000244140625, + "1": 46 + }, + "flags": {}, + "order": 6, + "mode": 0, + "inputs": [ + { + "name": "float_image_1", + "type": "FLOAT_IMAGE", + "link": 12 + }, + { + "name": "float_image_2", + "type": "FLOAT_IMAGE", + "link": 13 + } + ], + "outputs": [ + { + "name": "FLOAT_IMAGE", + "type": "FLOAT_IMAGE", + "links": [ + 32 + ], + "slot_index": 0 + } + ], + "properties": { + "Node name for S&R": "FloatImageCombine" + } + }, + { + "id": 19, + "type": "FloatImageCombine", + "pos": [ + 712, + 387 + ], + "size": { + "0": 317.4000244140625, + "1": 46 + }, + "flags": {}, + "order": 7, + "mode": 0, + "inputs": [ + { + "name": "float_image_1", + "type": "FLOAT_IMAGE", + "link": 32 + }, + { + "name": "float_image_2", + "type": "FLOAT_IMAGE", + "link": 22 + } + ], + "outputs": [ + { + "name": "FLOAT_IMAGE", + "type": "FLOAT_IMAGE", + "links": [ + 24 + ], + "slot_index": 0 + } + ], + "properties": { + "Node name for S&R": "FloatImageCombine" + } + }, + { + "id": 11, + "type": "ImageSetArea", + "pos": [ + 441, + 372 + ], + "size": { + "0": 216.59999084472656, + "1": 26 + }, + "flags": {}, + "order": 4, + "mode": 0, + "inputs": [ + { + "name": "image", + "type": "IMAGE", + "link": 74, + "slot_index": 0 + } + ], + "outputs": [ + { + "name": "FLOAT_IMAGE", + "type": "FLOAT_IMAGE", + "links": [ + 13 + ], + "slot_index": 0 + } + ], + "properties": { + "Node name for S&R": "ImageSetArea" + } + }, + { + "id": 18, + "type": "ImageSetArea", + "pos": [ + 439, + 439 + ], + "size": { + "0": 216.59999084472656, + "1": 26 + }, + "flags": {}, + "order": 5, + "mode": 0, + "inputs": [ + { + "name": "image", + "type": "IMAGE", + "link": 75 + } + ], + "outputs": [ + { + "name": "FLOAT_IMAGE", + "type": "FLOAT_IMAGE", + "links": [ + 22 + ], + "slot_index": 0 + } + ], + "properties": { + "Node name for S&R": "ImageSetArea" + } + }, + { + "id": 41, + "type": "LoadImage", + "pos": [ + 69, + 307 + ], + "size": { + "0": 315, + "1": 102 + }, + "flags": {}, + "order": 0, + "mode": 0, + "outputs": [ + { + "name": "IMAGE", + "type": "IMAGE", + "links": [ + 73 + ], + "slot_index": 0 + }, + { + "name": "MASK", + "type": "MASK", + "links": null + } + ], + "properties": { + "Node name for S&R": "LoadImage" + }, + "widgets_values": [ + "ComfyUI_00171_.png", + "image" + ] + }, + { + "id": 43, + "type": "LoadImage", + "pos": [ + 68, + 592 + ], + "size": { + "0": 315, + "1": 102 + }, + "flags": {}, + "order": 2, + "mode": 0, + "outputs": [ + { + "name": "IMAGE", + "type": "IMAGE", + "links": [ + 75 + ], + "slot_index": 0 + }, + { + "name": "MASK", + "type": "MASK", + "links": null + } + ], + "properties": { + "Node name for S&R": "LoadImage" + }, + "widgets_values": [ + "ComfyUI_00116_.png", + "image" + ] + }, + { + "id": 17, + "type": "XYPlot", + "pos": [ + 1075, + 326 + ], + "size": { + "0": 315, + "1": 82 + }, + "flags": {}, + "order": 8, + "mode": 0, + "inputs": [ + { + "name": "float_image", + "type": "FLOAT_IMAGE", + "link": 24 + } + ], + "outputs": [ + { + "name": "IMAGE", + "type": "IMAGE", + "links": [ + 20 + ], + "slot_index": 0 + } + ], + "properties": { + "Node name for S&R": "XYPlot" + }, + "widgets_values": [ + 10, + 2 + ] + }, + { + "id": 42, + "type": "LoadImage", + "pos": [ + 69, + 449 + ], + "size": { + "0": 315, + "1": 102 + }, + "flags": {}, + "order": 1, + "mode": 0, + "outputs": [ + { + "name": "IMAGE", + "type": "IMAGE", + "links": [ + 74 + ], + "slot_index": 0 + }, + { + "name": "MASK", + "type": "MASK", + "links": null + } + ], + "properties": { + "Node name for S&R": "LoadImage" + }, + "widgets_values": [ + "ComfyUI_00153_.png", + "image" + ] + } + ], + "links": [ + [ + 12, + 10, + 0, + 12, + 0, + "FLOAT_IMAGE" + ], + [ + 13, + 11, + 0, + 12, + 1, + "FLOAT_IMAGE" + ], + [ + 20, + 17, + 0, + 15, + 0, + "IMAGE" + ], + [ + 22, + 18, + 0, + 19, + 1, + "FLOAT_IMAGE" + ], + [ + 24, + 19, + 0, + 17, + 0, + "FLOAT_IMAGE" + ], + [ + 32, + 12, + 0, + 19, + 0, + "FLOAT_IMAGE" + ], + [ + 73, + 41, + 0, + 10, + 0, + "IMAGE" + ], + [ + 74, + 42, + 0, + 11, + 0, + "IMAGE" + ], + [ + 75, + 43, + 0, + 18, + 0, + "IMAGE" + ] + ], + "groups": [], + "config": {}, + "extra": {}, + "version": 0.4 +} \ No newline at end of file diff --git a/workflows/xy_plot_mini.png b/workflows/xy_plot_mini.png new file mode 100644 index 0000000..2832dff Binary files /dev/null and b/workflows/xy_plot_mini.png differ