| |
| |
|
|
| from comfy.sd import ModelPatcher, CLIP, VAE |
| from nodes import common_ksampler, CLIPSetLastLayer |
|
|
| from torch import Tensor |
| from PIL import Image, ImageOps, ImageDraw, ImageFont |
| from PIL.PngImagePlugin import PngInfo |
| import numpy as np |
| import torch |
|
|
| import ast |
| from pathlib import Path |
| import os |
| import sys |
| import subprocess |
| import json |
| import folder_paths |
| import psutil |
|
|
| |
| my_dir = os.path.dirname(os.path.abspath(__file__)) |
|
|
| |
| sys.path.append(my_dir) |
|
|
| |
| comfy_dir = os.path.abspath(os.path.join(my_dir, '..', '..')) |
|
|
| |
| sys.path.append(comfy_dir) |
|
|
| |
| font_path = os.path.join(my_dir, 'arial.ttf') |
|
|
| |
| import comfy.samplers |
| import comfy.sd |
| import comfy.utils |
|
|
| |
| from tsc_utils import * |
|
|
| MAX_RESOLUTION=8192 |
|
|
| |
| |
| class TSC_EfficientLoader: |
|
|
| @classmethod |
| def INPUT_TYPES(cls): |
| return {"required": { "ckpt_name": (folder_paths.get_filename_list("checkpoints"),), |
| "vae_name": (["Baked VAE"] + folder_paths.get_filename_list("vae"),), |
| "clip_skip": ("INT", {"default": -1, "min": -24, "max": -1, "step": 1}), |
| "lora_name": (["None"] + folder_paths.get_filename_list("loras"),), |
| "lora_model_strength": ("FLOAT", {"default": 1.0, "min": -10.0, "max": 10.0, "step": 0.01}), |
| "lora_clip_strength": ("FLOAT", {"default": 1.0, "min": -10.0, "max": 10.0, "step": 0.01}), |
| "positive": ("STRING", {"default": "Positive","multiline": True}), |
| "negative": ("STRING", {"default": "Negative", "multiline": True}), |
| "empty_latent_width": ("INT", {"default": 512, "min": 64, "max": MAX_RESOLUTION, "step": 64}), |
| "empty_latent_height": ("INT", {"default": 512, "min": 64, "max": MAX_RESOLUTION, "step": 64}), |
| "batch_size": ("INT", {"default": 1, "min": 1, "max": 64})}, |
| "optional": {"lora_stack": ("LORA_STACK", )}, |
| "hidden": { "prompt": "PROMPT", |
| "my_unique_id": "UNIQUE_ID",}, |
| } |
|
|
| RETURN_TYPES = ("MODEL", "CONDITIONING", "CONDITIONING", "LATENT", "VAE", "CLIP", "DEPENDENCIES",) |
| RETURN_NAMES = ("MODEL", "CONDITIONING+", "CONDITIONING-", "LATENT", "VAE", "CLIP", "DEPENDENCIES", ) |
| FUNCTION = "efficientloader" |
| CATEGORY = "Efficiency Nodes/Loaders" |
|
|
| def efficientloader(self, ckpt_name, vae_name, clip_skip, lora_name, lora_model_strength, lora_clip_strength, |
| positive, negative, empty_latent_width, empty_latent_height, batch_size, lora_stack=None, |
| prompt=None, my_unique_id=None): |
|
|
| model: ModelPatcher | None = None |
| clip: CLIP | None = None |
| vae: VAE | None = None |
|
|
| |
| latent = torch.zeros([batch_size, 4, empty_latent_height // 8, empty_latent_width // 8]).cpu() |
|
|
| |
| globals_cleanup(prompt) |
|
|
| |
| vae_cache, ckpt_cache, lora_cache = get_cache_numbers("Efficient Loader") |
|
|
| if lora_name != "None": |
| lora_params = [(lora_name, lora_model_strength, lora_clip_strength)] |
| if lora_stack is not None: |
| lora_params.extend(lora_stack) |
| model, clip = load_lora(lora_params, ckpt_name, my_unique_id, cache=lora_cache, ckpt_cache=ckpt_cache, cache_overwrite=True) |
| if vae_name == "Baked VAE": |
| vae = get_bvae_by_ckpt_name(ckpt_name) |
| else: |
| model, clip, vae = load_checkpoint(ckpt_name, my_unique_id, cache=ckpt_cache, cache_overwrite=True) |
| lora_params = None |
|
|
| |
| if vae_name != "Baked VAE": |
| vae = load_vae(vae_name, my_unique_id, cache=vae_cache, cache_overwrite=True) |
|
|
| |
| |
|
|
| |
| if not clip: |
| raise Exception("No CLIP found") |
| clip = clip.clone() |
| clip.clip_layer(clip_skip) |
|
|
| |
| dependencies = (vae_name, ckpt_name, clip, clip_skip, positive, negative, lora_params) |
|
|
| return (model, [[clip.encode(positive), {}]], [[clip.encode(negative), {}]], {"samples":latent}, vae, clip, dependencies, ) |
|
|
| |
| |
| class TSC_LoRA_Stacker: |
|
|
| loras = ["None"] + folder_paths.get_filename_list("loras") |
|
|
| @classmethod |
| def INPUT_TYPES(cls): |
| return {"required": { |
| "lora_name_1": (cls.loras,), |
| "lora_wt_1": ("FLOAT", {"default": 1.0, "min": -10.0, "max": 10.0, "step": 0.01}), |
| "lora_name_2": (cls.loras,), |
| "lora_wt_2": ("FLOAT", {"default": 1.0, "min": -10.0, "max": 10.0, "step": 0.01}), |
| "lora_name_3": (cls.loras,), |
| "lora_wt_3": ("FLOAT", {"default": 1.0, "min": -10.0, "max": 10.0, "step": 0.01})}, |
| "optional": {"lora_stack": ("LORA_STACK",)}, |
| } |
|
|
| RETURN_TYPES = ("LORA_STACK",) |
| RETURN_NAMES = ("LORA_STACK",) |
| FUNCTION = "lora_stacker" |
| CATEGORY = "Efficiency Nodes/Misc" |
|
|
| def lora_stacker(self, lora_name_1, lora_wt_1, lora_name_2, lora_wt_2, lora_name_3, lora_wt_3, lora_stack=None): |
| |
| loras = [(lora_name, lora_wt, lora_wt) for lora_name, lora_wt, lora_wt in |
| [(lora_name_1, lora_wt_1, lora_wt_1), |
| (lora_name_2, lora_wt_2, lora_wt_2), |
| (lora_name_3, lora_wt_3, lora_wt_3)] |
| if lora_name != "None"] |
|
|
| |
| if lora_stack is not None: |
| loras.extend([l for l in lora_stack if l[0] != "None"]) |
|
|
| return (loras,) |
|
|
| |
| class TSC_LoRA_Stacker_Adv: |
|
|
| loras = ["None"] + folder_paths.get_filename_list("loras") |
|
|
| @classmethod |
| def INPUT_TYPES(cls): |
| return {"required": { |
| "lora_name_1": (cls.loras,), |
| "model_str_1": ("FLOAT", {"default": 1.0, "min": -10.0, "max": 10.0, "step": 0.01}), |
| "clip_str_1": ("FLOAT", {"default": 1.0, "min": -10.0, "max": 10.0, "step": 0.01}), |
| "lora_name_2": (cls.loras,), |
| "model_str_2": ("FLOAT", {"default": 1.0, "min": -10.0, "max": 10.0, "step": 0.01}), |
| "clip_str_2": ("FLOAT", {"default": 1.0, "min": -10.0, "max": 10.0, "step": 0.01}), |
| "lora_name_3": (cls.loras,), |
| "model_str_3": ("FLOAT", {"default": 1.0, "min": -10.0, "max": 10.0, "step": 0.01}), |
| "clip_str_3": ("FLOAT", {"default": 1.0, "min": -10.0, "max": 10.0, "step": 0.01})}, |
| "optional": {"lora_stack": ("LORA_STACK",)}, |
| } |
|
|
| RETURN_TYPES = ("LORA_STACK",) |
| RETURN_NAMES = ("LORA_STACK",) |
| FUNCTION = "lora_stacker" |
| CATEGORY = "Efficiency Nodes/Misc" |
|
|
| def lora_stacker(self, lora_name_1, model_str_1, clip_str_1, lora_name_2, model_str_2, clip_str_2, |
| lora_name_3, model_str_3, clip_str_3, lora_stack=None): |
| |
| loras = [(lora_name, model_str, clip_str) for lora_name, model_str, clip_str in |
| [(lora_name_1, model_str_1, clip_str_1), |
| (lora_name_2, model_str_2, clip_str_2), |
| (lora_name_3, model_str_3, clip_str_3)] |
| if lora_name != "None"] |
|
|
| |
| if lora_stack is not None: |
| loras.extend([l for l in lora_stack if l[0] != "None"]) |
|
|
| return (loras,) |
|
|
| |
| |
| class TSC_KSampler: |
| |
| empty_image = pil2tensor(Image.new('RGBA', (1, 1), (0, 0, 0, 0))) |
|
|
| def __init__(self): |
| self.output_dir = os.path.join(comfy_dir, 'temp') |
| self.type = "temp" |
|
|
| @classmethod |
| def INPUT_TYPES(cls): |
| return {"required": |
| {"sampler_state": (["Sample", "Hold", "Script"], ), |
| "model": ("MODEL",), |
| "seed": ("INT", {"default": 0, "min": 0, "max": 0xffffffffffffffff}), |
| "steps": ("INT", {"default": 20, "min": 1, "max": 10000}), |
| "cfg": ("FLOAT", {"default": 7.0, "min": 0.0, "max": 100.0}), |
| "sampler_name": (comfy.samplers.KSampler.SAMPLERS,), |
| "scheduler": (comfy.samplers.KSampler.SCHEDULERS,), |
| "positive": ("CONDITIONING",), |
| "negative": ("CONDITIONING",), |
| "latent_image": ("LATENT",), |
| "denoise": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.01}), |
| "preview_image": (["Disabled", "Enabled", "Output Only"],), |
| }, |
| "optional": { "optional_vae": ("VAE",), |
| "script": ("SCRIPT",),}, |
| "hidden": {"prompt": "PROMPT", "extra_pnginfo": "EXTRA_PNGINFO", "my_unique_id": "UNIQUE_ID",}, |
| } |
|
|
| RETURN_TYPES = ("MODEL", "CONDITIONING", "CONDITIONING", "LATENT", "VAE", "IMAGE", ) |
| RETURN_NAMES = ("MODEL", "CONDITIONING+", "CONDITIONING-", "LATENT", "VAE", "IMAGE", ) |
| OUTPUT_NODE = True |
| FUNCTION = "sample" |
| CATEGORY = "Efficiency Nodes/Sampling" |
| |
| def sample(self, sampler_state, model, seed, steps, cfg, sampler_name, scheduler, positive, negative, |
| latent_image, preview_image, denoise=1.0, prompt=None, extra_pnginfo=None, my_unique_id=None, |
| optional_vae=(None,), script=None): |
|
|
| |
| def get_settings(): |
| |
| my_dir = os.path.dirname(os.path.abspath(__file__)) |
| |
| settings_file = os.path.join(my_dir, 'node_settings.json') |
| |
| with open(settings_file, 'r') as file: |
| node_settings = json.load(file) |
| |
| kse_vae_tiled = node_settings.get("KSampler (Efficient)", {}).get('vae_tiled', False) |
| xy_vae_tiled = node_settings.get("XY Plot", {}).get('vae_tiled', False) |
| return kse_vae_tiled, xy_vae_tiled |
|
|
| kse_vae_tiled, xy_vae_tiled = get_settings() |
|
|
| |
| def map_filename(filename): |
| prefix_len = len(os.path.basename(filename_prefix)) |
| prefix = filename[:prefix_len + 1] |
| try: |
| digits = int(filename[prefix_len + 1:].split('_')[0]) |
| except: |
| digits = 0 |
| return (digits, prefix) |
|
|
| def compute_vars(input): |
| input = input.replace("%width%", str(images[0].shape[1])) |
| input = input.replace("%height%", str(images[0].shape[0])) |
| return input |
|
|
| def preview_images(images, filename_prefix): |
| filename_prefix = compute_vars(filename_prefix) |
|
|
| subfolder = os.path.dirname(os.path.normpath(filename_prefix)) |
| filename = os.path.basename(os.path.normpath(filename_prefix)) |
|
|
| full_output_folder = os.path.join(self.output_dir, subfolder) |
|
|
| try: |
| counter = max(filter(lambda a: a[1][:-1] == filename and a[1][-1] == "_", |
| map(map_filename, os.listdir(full_output_folder))))[0] + 1 |
| except ValueError: |
| counter = 1 |
| except FileNotFoundError: |
| os.makedirs(full_output_folder, exist_ok=True) |
| counter = 1 |
|
|
| if not os.path.exists(self.output_dir): |
| os.makedirs(self.output_dir) |
|
|
| results = list() |
| for image in images: |
| i = 255. * image.cpu().numpy() |
| img = Image.fromarray(np.clip(i, 0, 255).astype(np.uint8)) |
| metadata = PngInfo() |
| if prompt is not None: |
| metadata.add_text("prompt", json.dumps(prompt)) |
| if extra_pnginfo is not None: |
| for x in extra_pnginfo: |
| metadata.add_text(x, json.dumps(extra_pnginfo[x])) |
| file = f"{filename}_{counter:05}_.png" |
| img.save(os.path.join(full_output_folder, file), pnginfo=metadata, compress_level=4) |
| results.append({ |
| "filename": file, |
| "subfolder": subfolder, |
| "type": self.type |
| }); |
| counter += 1 |
| return results |
|
|
| def get_value_by_id(key: str, my_unique_id): |
| global last_helds |
| for value, id_ in last_helds[key]: |
| if id_ == my_unique_id: |
| return value |
| return None |
|
|
| def update_value_by_id(key: str, my_unique_id, new_value): |
| global last_helds |
|
|
| for i, (value, id_) in enumerate(last_helds[key]): |
| if id_ == my_unique_id: |
| last_helds[key][i] = (new_value, id_) |
| return True |
|
|
| last_helds[key].append((new_value, my_unique_id)) |
| return True |
|
|
| |
| globals_cleanup(prompt) |
|
|
| |
| my_unique_id = int(my_unique_id) |
|
|
| |
| vae = optional_vae |
| if vae == (None,): |
| print('\033[33mKSampler(Efficient) Warning:\033[0m No vae input detected, preview and output image disabled.\n') |
| preview_image = "Disabled" |
|
|
| |
| if get_value_by_id("results", my_unique_id) is None: |
| last_results = list() |
| else: |
| last_results = get_value_by_id("results", my_unique_id) |
|
|
| |
| if get_value_by_id("latent", my_unique_id) is None: |
| last_latent = latent_image |
| else: |
| last_latent = {"samples": None} |
| last_latent["samples"] = get_value_by_id("latent", my_unique_id) |
|
|
| |
| if get_value_by_id("images", my_unique_id) == None: |
| last_images = TSC_KSampler.empty_image |
| else: |
| last_images = get_value_by_id("images", my_unique_id) |
|
|
| |
| latent: Tensor|None = None |
|
|
| |
| filename_prefix = "KSeff_{:02d}".format(my_unique_id) |
|
|
| |
| |
| if sampler_state == "Sample": |
|
|
| |
| samples = common_ksampler(model, seed, steps, cfg, sampler_name, scheduler, positive, negative, |
| latent_image, denoise=denoise) |
|
|
| |
| latent = samples[0]["samples"] |
|
|
| |
| update_value_by_id("latent", my_unique_id, latent) |
|
|
| |
| if preview_image == "Disabled": |
| |
| update_value_by_id("vae_decode", my_unique_id, True) |
| return {"ui": {"images": list()}, |
| "result": (model, positive, negative, {"samples": latent}, vae, TSC_KSampler.empty_image,)} |
| else: |
| |
| if kse_vae_tiled == False: |
| images = vae.decode(latent).cpu() |
| else: |
| images = vae.decode_tiled(latent).cpu() |
| update_value_by_id("images", my_unique_id, images) |
|
|
| |
| update_value_by_id("vae_decode", my_unique_id, False) |
|
|
| |
| results = preview_images(images, filename_prefix) |
| update_value_by_id("results", my_unique_id, results) |
|
|
| |
| images_value = list() if preview_image == "Output Only" else results |
|
|
| |
| return {"ui": {"images": images_value}, |
| "result": (model, positive, negative, {"samples": latent}, vae, images,)} |
|
|
|
|
| |
| |
| elif sampler_state == "Hold": |
|
|
| |
| if preview_image == "Disabled": |
| return {"ui": {"images": list()}, |
| "result": (model, positive, negative, last_latent, vae, TSC_KSampler.empty_image,)} |
|
|
| else: |
| latent = last_latent["samples"] |
|
|
| if get_value_by_id("vae_decode", my_unique_id) == True: |
|
|
| |
| if kse_vae_tiled == False: |
| images = vae.decode(latent).cpu() |
| else: |
| images = vae.decode_tiled(latent).cpu() |
| update_value_by_id("images", my_unique_id, images) |
|
|
| |
| update_value_by_id("vae_decode", my_unique_id, False) |
|
|
| |
| results = preview_images(images, filename_prefix) |
| update_value_by_id("results", my_unique_id, results) |
|
|
| else: |
| images = last_images |
| results = last_results |
|
|
| |
| images_value = list() if preview_image == "Output Only" else results |
|
|
| |
| return {"ui": {"images": images_value}, |
| "result": (model, positive, negative, {"samples": latent}, vae, images,)} |
|
|
| |
| elif sampler_state == "Script": |
|
|
| |
| script_node_name, script_node_id = extract_node_info(prompt, my_unique_id, 'script') |
|
|
| |
| if script == None or script == (None,) or script_node_name!="XY Plot": |
| if script_node_name!="XY Plot": |
| print('\033[31mKSampler(Efficient) Error:\033[0m No valid script input detected') |
| return {"ui": {"images": list()}, |
| "result": (model, positive, negative, last_latent, vae, last_images,)} |
|
|
| |
| if vae == (None,): |
| print('\033[31mKSampler(Efficient) Error:\033[0m VAE must be connected to use Script mode.') |
| return {"ui": {"images": list()}, |
| "result": (model, positive, negative, last_latent, vae, last_images,)} |
|
|
| |
| if preview_image == "Disabled": |
| print('\033[33mKSampler(Efficient) Warning:\033[0m The preview image cannot be disabled when running' |
| ' the XY Plot script, proceeding as if it was enabled.\n') |
|
|
| |
| image_tensors = torch.split(latent_image['samples'], 1, dim=0) |
|
|
| |
| shape = image_tensors[0].shape |
|
|
| |
| latent_height, latent_width = shape[2] * 8, shape[3] * 8 |
|
|
| |
| latent_image = {'samples': image_tensors[0]} |
|
|
| |
| |
| if script_node_name == "XY Plot": |
|
|
| |
| vae_name = None |
| ckpt_name = None |
| clip = None |
| lora_params = None |
| positive_prompt = None |
| negative_prompt = None |
| clip_skip = None |
|
|
| |
| X_type, X_value, Y_type, Y_value, grid_spacing, Y_label_orientation, cache_models, xyplot_as_output_image,\ |
| flip_xy, dependencies = script |
|
|
| |
| if dependencies is not None: |
| vae_name, ckpt_name, clip, clip_skip, positive_prompt, negative_prompt, lora_params = dependencies |
|
|
| |
| def process_xy_for_print(value, replacement, type_): |
| if isinstance(value, tuple) and type_ == "Scheduler": |
| return value[0] |
| elif isinstance(value, tuple): |
| return tuple(replacement if v is None else v for v in value) |
| else: |
| return replacement if value is None else value |
|
|
| |
| replacement_X = scheduler if X_type == 'Sampler' else clip_skip if X_type == 'Checkpoint' else None |
| replacement_Y = scheduler if Y_type == 'Sampler' else clip_skip if Y_type == 'Checkpoint' else None |
|
|
| |
| X_value_processed = [process_xy_for_print(v, replacement_X, X_type) for v in X_value] |
| Y_value_processed = [process_xy_for_print(v, replacement_Y, Y_type) for v in Y_value] |
|
|
| |
| print("-" * 40) |
| print("XY Plot Script Inputs:") |
| print(f"(X) {X_type}: {X_value_processed}") |
| print(f"(Y) {Y_type}: {Y_value_processed}") |
| print("-" * 40) |
|
|
| |
| if cache_models == "False": |
| vae_cache = ckpt_cache = lora_cache = 1 |
| else: |
| |
| vae_cache, ckpt_cache, lora_cache = get_cache_numbers("XY Plot") |
| |
| cache = (vae_cache, ckpt_cache, lora_cache) |
|
|
| |
| positive_prompt = (positive_prompt, positive_prompt) |
| negative_prompt = (negative_prompt, negative_prompt) |
|
|
| |
| |
|
|
| |
| dict_map = {"VAE": [], "Checkpoint": [], "LoRA": []} |
|
|
| |
| type_value_pairs = [(X_type, X_value), (Y_type, Y_value)] |
|
|
| |
| for t, v in type_value_pairs: |
| if t in dict_map: |
| |
| if t == "LoRA": |
| dict_map[t] = [item for sublist in v for item in sublist] |
| else: |
| dict_map[t] = v |
|
|
| ckpt_dict = [t[0] for t in dict_map.get("Checkpoint", [])] if dict_map.get("Checkpoint", []) else [] |
|
|
| lora_dict = [[t,] for t in dict_map.get("LoRA", [])] if dict_map.get("LoRA", []) else [] |
|
|
| |
| if ckpt_dict and lora_dict: |
| lora_dict = [(lora_params, ckpt) for ckpt in ckpt_dict for lora_params in lora_dict] |
| |
| elif lora_dict: |
| lora_dict = [(lora_params, ckpt_name) for lora_params in lora_dict] |
|
|
| vae_dict = dict_map.get("VAE", []) |
|
|
| |
| if X_type == "LoRA": |
| ckpt_dict = [] |
| if X_type == "Checkpoint": |
| lora_dict = [] |
|
|
| |
| |
|
|
| |
| clear_cache_by_exception(script_node_id, vae_dict=vae_dict, ckpt_dict=ckpt_dict, lora_dict=lora_dict) |
|
|
| |
| |
|
|
| |
| |
| def define_variable(var_type, var, seed, steps, cfg, sampler_name, scheduler, denoise, vae_name, ckpt_name, |
| clip_skip, positive_prompt, negative_prompt, lora_params, var_label, num_label): |
|
|
| |
| max_label_len = 36 |
|
|
| |
| if var_type == "Seeds++ Batch": |
| text = f"Seed: {seed}" |
|
|
| |
| elif var_type == "Steps": |
| steps = var |
| text = f"steps: {steps}" |
|
|
| |
| elif var_type == "CFG Scale": |
| cfg = var |
| text = f"CFG: {round(cfg,2)}" |
|
|
| |
| elif var_type == "Sampler": |
| sampler_name = var[0] |
| if var[1] == "": |
| text = f"{sampler_name}" |
| else: |
| if var[1] != None: |
| scheduler = (var[1], scheduler[1]) |
| else: |
| scheduler = (scheduler[1], scheduler[1]) |
| text = f"{sampler_name} ({scheduler[0]})" |
| text = text.replace("ancestral", "a").replace("uniform", "u").replace("exponential","exp") |
|
|
| |
| elif var_type == "Scheduler": |
| if len(var) == 2: |
| scheduler = (var[0], scheduler[1]) |
| text = f"{sampler_name} ({scheduler[0]})" |
| else: |
| scheduler = (var, scheduler[1]) |
| text = f"{scheduler[0]}" |
| text = text.replace("ancestral", "a").replace("uniform", "u").replace("exponential","exp") |
|
|
| |
| elif var_type == "Denoise": |
| denoise = var |
| text = f"denoise: {round(denoise, 2)}" |
|
|
| |
| elif var_type == "VAE": |
| vae_name = var |
| vae_filename = os.path.splitext(os.path.basename(vae_name))[0] |
| text = f"VAE: {vae_filename}" |
|
|
| |
| elif var_type == "Positive Prompt S/R": |
| search_txt, replace_txt = var |
| if replace_txt != None: |
| positive_prompt = (positive_prompt[1].replace(search_txt, replace_txt, 1), positive_prompt[1]) |
| else: |
| positive_prompt = (positive_prompt[1], positive_prompt[1]) |
| replace_txt = search_txt |
| text = f"{replace_txt}" |
|
|
| |
| elif var_type == "Negative Prompt S/R": |
| search_txt, replace_txt = var |
| if replace_txt: |
| negative_prompt = (negative_prompt[1].replace(search_txt, replace_txt, 1), negative_prompt[1]) |
| else: |
| negative_prompt = (negative_prompt[1], negative_prompt[1]) |
| replace_txt = search_txt |
| text = f"(-) {replace_txt}" |
|
|
| |
| elif var_type == "Checkpoint": |
| ckpt_name = var[0] |
| if var[1] == None: |
| clip_skip = (clip_skip[1],clip_skip[1]) |
| else: |
| clip_skip = (var[1],clip_skip[1]) |
| ckpt_filename = os.path.splitext(os.path.basename(ckpt_name))[0] |
| text = f"{ckpt_filename}" |
|
|
| elif var_type == "Clip Skip": |
| clip_skip = (var, clip_skip[1]) |
| text = f"Clip Skip ({clip_skip[0]})" |
|
|
| elif var_type == "LoRA": |
| lora_params = var |
| max_label_len = 30 + (12 * (len(lora_params)-1)) |
| if len(lora_params) == 1: |
| lora_name, lora_model_wt, lora_clip_wt = lora_params[0] |
| lora_filename = os.path.splitext(os.path.basename(lora_name))[0] |
| lora_model_wt = format(float(lora_model_wt), ".2f").rstrip('0').rstrip('.') |
| lora_clip_wt = format(float(lora_clip_wt), ".2f").rstrip('0').rstrip('.') |
| lora_filename = lora_filename[:max_label_len - len(f"LoRA: ({lora_model_wt})")] |
| if lora_model_wt == lora_clip_wt: |
| text = f"LoRA: {lora_filename}({lora_model_wt})" |
| else: |
| text = f"LoRA: {lora_filename}({lora_model_wt},{lora_clip_wt})" |
| elif len(lora_params) > 1: |
| lora_filenames = [os.path.splitext(os.path.basename(lora_name))[0] for lora_name, _, _ in lora_params] |
| lora_details = [(format(float(lora_model_wt), ".2f").rstrip('0').rstrip('.'), |
| format(float(lora_clip_wt), ".2f").rstrip('0').rstrip('.')) for _, lora_model_wt, lora_clip_wt in lora_params] |
| non_name_length = sum(len(f"({lora_details[i][0]},{lora_details[i][1]})") + 2 for i in range(len(lora_params))) |
| available_space = max_label_len - non_name_length |
| max_name_length = available_space // len(lora_params) |
| lora_filenames = [filename[:max_name_length] for filename in lora_filenames] |
| text_elements = [f"{lora_filename}({lora_details[i][0]})" if lora_details[i][0] == lora_details[i][1] else f"{lora_filename}({lora_details[i][0]},{lora_details[i][1]})" for i, lora_filename in enumerate(lora_filenames)] |
| text = " ".join(text_elements) |
|
|
| def truncate_texts(texts, num_label, max_label_len): |
| truncate_length = max(min(max(len(text) for text in texts), max_label_len), 24) |
|
|
| return [text if len(text) <= truncate_length else text[:truncate_length] + "..." for text in |
| texts] |
|
|
| |
| if len(var_label) < num_label: |
| var_label.append(text) |
|
|
| |
| if len(var_label) == num_label and (var_type == "VAE" or var_type == "Checkpoint" or var_type == "LoRA"): |
| var_label = truncate_texts(var_label, num_label, max_label_len) |
|
|
| |
| return steps, cfg, sampler_name, scheduler, denoise, vae_name, ckpt_name, clip_skip, \ |
| positive_prompt, negative_prompt, lora_params, var_label |
|
|
| |
| |
| def define_model(model, clip, positive, negative, positive_prompt, negative_prompt, clip_skip, vae, |
| vae_name, ckpt_name, lora_params, index, types, script_node_id, cache): |
| |
| |
| def encode_prompt(positive_prompt, negative_prompt, clip, clip_skip): |
| clip = CLIPSetLastLayer().set_last_layer(clip, clip_skip)[0] |
| return [[clip.encode(positive_prompt), {}]], [[clip.encode(negative_prompt), {}]] |
|
|
| |
| encode = False |
|
|
| |
| X_type, Y_type = types |
|
|
| |
|
|
| |
| if (X_type == "VAE" and index == 0) or Y_type == "VAE": |
| vae = load_vae(vae_name, script_node_id, cache=cache[0]) |
|
|
| |
| if (X_type == "Checkpoint" and index == 0 and Y_type != "LoRA"): |
| if lora_params is None: |
| model, clip, _ = load_checkpoint(ckpt_name, script_node_id, output_vae=False, cache=cache[1]) |
| else: |
| model, clip = load_lora(lora_params, ckpt_name, script_node_id, |
| cache=None, ckpt_cache=cache[1]) |
| encode = True |
|
|
| |
| elif (X_type == "LoRA" and index == 0): |
| |
| model, clip = load_lora(lora_params, ckpt_name, script_node_id, cache=cache[2]) |
| encode = True |
| |
| elif Y_type == "LoRA": |
| model, clip = load_lora(lora_params, ckpt_name, script_node_id, |
| cache=None, ckpt_cache=cache[1]) |
| encode = True |
|
|
| |
| prompt_types = ["Positive Prompt S/R", "Negative Prompt S/R", "Clip Skip"] |
| if (X_type in prompt_types and index == 0) or Y_type in prompt_types: |
| encode = True |
|
|
| |
| if encode == True: |
| positive, negative = encode_prompt(positive_prompt[0], negative_prompt[0], clip, clip_skip) |
| |
| return model, positive, negative, vae |
|
|
| |
| |
| def process_values(model, seed, steps, cfg, sampler_name, scheduler, positive, negative, latent_image, |
| denoise, vae, latent_list=[], image_tensor_list=[], image_pil_list=[]): |
|
|
| |
| samples = common_ksampler(model, seed, steps, cfg, sampler_name, scheduler, positive, negative, |
| latent_image, denoise=denoise) |
|
|
| |
| latent = samples[0]["samples"] |
|
|
| |
| latent_list.append(latent) |
|
|
| |
| if xy_vae_tiled == False: |
| image = vae.decode(latent).cpu() |
| else: |
| image = vae.decode_tiled(latent).cpu() |
|
|
| |
| image_tensor_list.append(image) |
|
|
| |
| image_pil_list.append(tensor2pil(image)) |
|
|
| |
| return latent_list, image_tensor_list, image_pil_list |
|
|
| |
| |
|
|
| |
| X_label = [] |
| Y_label = [] |
|
|
| |
| seed_updated = seed |
|
|
| |
| scheduler = (scheduler, scheduler) |
|
|
| |
| clip_skip = (clip_skip, clip_skip) |
|
|
| |
| types = (X_type, Y_type) |
|
|
| |
| for X_index, X in enumerate(X_value): |
|
|
| |
| if X_type == "Seeds++ Batch": |
| |
| seed_updated = seed + X_index |
|
|
| |
| steps, cfg, sampler_name, scheduler, denoise, vae_name, ckpt_name, clip_skip, positive_prompt, negative_prompt, \ |
| lora_params, X_label = \ |
| define_variable(X_type, X, seed_updated, steps, cfg, sampler_name, scheduler, denoise, vae_name, ckpt_name, |
| clip_skip, positive_prompt, negative_prompt, lora_params, X_label, len(X_value)) |
|
|
| if X_type != "Nothing" and Y_type == "Nothing": |
|
|
| |
| model, positive, negative , vae = \ |
| define_model(model, clip, positive, negative, positive_prompt, negative_prompt, clip_skip[0], vae, |
| vae_name, ckpt_name, lora_params, 0, types, script_node_id, cache) |
|
|
| |
| latent_list, image_tensor_list, image_pil_list = \ |
| process_values(model, seed_updated, steps, cfg, sampler_name, scheduler[0], |
| positive, negative, latent_image, denoise, vae) |
|
|
| elif X_type != "Nothing" and Y_type != "Nothing": |
| |
| for Y_index, Y in enumerate(Y_value): |
|
|
| if Y_type == "Seeds++ Batch": |
| |
| seed_updated = seed + Y_index |
|
|
| |
| steps, cfg, sampler_name, scheduler, denoise, vae_name, ckpt_name, clip_skip, positive_prompt, negative_prompt, lora_params, Y_label = \ |
| define_variable(Y_type, Y, seed_updated, steps, cfg, sampler_name, scheduler, denoise, vae_name, ckpt_name, |
| clip_skip, positive_prompt, negative_prompt, lora_params, Y_label, len(Y_value)) |
|
|
| |
| model, positive, negative, vae = \ |
| define_model(model, clip, positive, negative, positive_prompt, negative_prompt, clip_skip[0], vae, |
| vae_name, ckpt_name, lora_params, Y_index, types, script_node_id, cache) |
|
|
| |
| latent_list, image_tensor_list, image_pil_list = \ |
| process_values(model, seed_updated, steps, cfg, sampler_name, scheduler[0], |
| positive, negative, latent_image, denoise, vae) |
|
|
| |
| if cache_models == "False": |
| clear_cache_by_exception(script_node_id, vae_dict=[], ckpt_dict=[], lora_dict=[]) |
| |
| else: |
| |
| if X_type == "LoRA": |
| clear_cache_by_exception(script_node_id, ckpt_dict=[]) |
| elif X_type == "Checkpoint": |
| clear_cache_by_exception(script_node_id, lora_dict=[]) |
|
|
| |
| def print_plot_variables(X_type, Y_type, X_value, Y_value, seed, ckpt_name, lora_params, |
| vae_name, clip_skip, steps, cfg, sampler_name, scheduler, denoise, |
| num_rows, num_cols, latent_height, latent_width): |
|
|
| print("-" * 40) |
| print("\033[32mXY Plot Results:\033[0m") |
|
|
| def get_vae_name(X_type, Y_type, X_value, Y_value, vae_name): |
| if X_type == "VAE": |
| vae_name = ", ".join(map(lambda x: os.path.splitext(os.path.basename(str(x)))[0], X_value)) |
| elif Y_type == "VAE": |
| vae_name = ", ".join(map(lambda y: os.path.splitext(os.path.basename(str(y)))[0], Y_value)) |
| else: |
| vae_name = os.path.splitext(os.path.basename(str(vae_name)))[0] |
| return vae_name |
| |
| def get_clip_skip(X_type, Y_type, X_value, Y_value, clip_skip): |
| if X_type == "Clip Skip": |
| clip_skip = ", ".join(map(str, X_value)) |
| elif Y_type == "Clip Skip": |
| clip_skip = ", ".join(map(str, Y_value)) |
| else: |
| clip_skip = clip_skip[1] |
| return clip_skip |
|
|
| def get_checkpoint_name(ckpt_type, ckpt_values, clip_skip_type, clip_skip_values, ckpt_name, clip_skip): |
| if ckpt_type == "Checkpoint": |
| if clip_skip_type == "Clip Skip": |
| ckpt_name = ", ".join([os.path.splitext(os.path.basename(str(ckpt[0])))[0] for ckpt in ckpt_values]) |
| else: |
| ckpt_name = ", ".join([f"{os.path.splitext(os.path.basename(str(ckpt[0])))[0]}({str(ckpt[1]) if ckpt[1] is not None else str(clip_skip_values)})" |
| for ckpt in ckpt_values]) |
| clip_skip = "_" |
| else: |
| ckpt_name = os.path.splitext(os.path.basename(str(ckpt_name)))[0] |
|
|
| return ckpt_name, clip_skip |
|
|
| def get_lora_name(X_type, Y_type, X_value, Y_value, lora_params=None): |
| if X_type != "LoRA" and Y_type != "LoRA": |
| if lora_params: |
| return f"[{', '.join([f'{os.path.splitext(os.path.basename(name))[0]}({round(model_wt, 3)},{round(clip_wt, 3)})' for name, model_wt, clip_wt in lora_params])}]" |
| else: |
| return None |
| else: |
| return get_lora_sublist_name(X_type, |
| X_value) if X_type == "LoRA" else get_lora_sublist_name(Y_type, Y_value) if Y_type == "LoRA" else None |
|
|
| def get_lora_sublist_name(lora_type, lora_value): |
| return ", ".join([ |
| f"[{', '.join([f'{os.path.splitext(os.path.basename(str(x[0])))[0]}({round(x[1], 3)},{round(x[2], 3)})' for x in sublist])}]" |
| for sublist in lora_value]) |
|
|
| |
| ckpt_type, clip_skip_type = (X_type, Y_type) if X_type in ["Checkpoint", "Clip Skip"] else (Y_type, X_type) |
| ckpt_values, clip_skip_values = (X_value, Y_value) if X_type in ["Checkpoint", "Clip Skip"] else (Y_value, X_value) |
|
|
| clip_skip = get_clip_skip(X_type, Y_type, X_value, Y_value, clip_skip) |
| ckpt_name, clip_skip = get_checkpoint_name(ckpt_type, ckpt_values, clip_skip_type, clip_skip_values, ckpt_name, clip_skip) |
| vae_name = get_vae_name(X_type, Y_type, X_value, Y_value, vae_name) |
| lora_name = get_lora_name(X_type, Y_type, X_value, Y_value, lora_params) |
|
|
| seed_list = [seed + x for x in X_value] if X_type == "Seeds++ Batch" else\ |
| [seed + y for y in Y_value] if Y_type == "Seeds++ Batch" else [seed] |
| seed = ", ".join(map(str, seed_list)) |
|
|
| steps = ", ".join(map(str, X_value)) if X_type == "Steps" else ", ".join( |
| map(str, Y_value)) if Y_type == "Steps" else steps |
|
|
| cfg = ", ".join(map(str, X_value)) if X_type == "CFG Scale" else ", ".join( |
| map(str, Y_value)) if Y_type == "CFG Scale" else cfg |
|
|
| if X_type == "Sampler": |
| if Y_type == "Scheduler": |
| sampler_name = ", ".join([f"{x[0]}" for x in X_value]) |
| scheduler = ", ".join([f"{y}" for y in Y_value]) |
| else: |
| sampler_name = ", ".join( |
| [f"{x[0]}({x[1] if x[1] != '' and x[1] is not None else scheduler[1]})" for x in X_value]) |
| scheduler = "_" |
| elif Y_type == "Sampler": |
| if X_type == "Scheduler": |
| sampler_name = ", ".join([f"{y[0]}" for y in Y_value]) |
| scheduler = ", ".join([f"{x}" for x in X_value]) |
| else: |
| sampler_name = ", ".join( |
| [f"{y[0]}({y[1] if y[1] != '' and y[1] is not None else scheduler[1]})" for y in Y_value]) |
| scheduler = "_" |
| else: |
| scheduler = ", ".join([str(x[0]) if isinstance(x, tuple) else str(x) for x in X_value]) if X_type == "Scheduler" else \ |
| ", ".join([str(y[0]) if isinstance(y, tuple) else str(y) for y in Y_value]) if Y_type == "Scheduler" else scheduler[0] |
|
|
| denoise = ", ".join(map(str, X_value)) if X_type == "Denoise" else ", ".join( |
| map(str, Y_value)) if Y_type == "Denoise" else denoise |
|
|
| |
| print(f"img_count: {len(X_value)*len(Y_value)}") |
| print(f"img_dims: {latent_height} x {latent_width}") |
| print(f"plot_dim: {num_cols} x {num_rows}") |
| if clip_skip == "_": |
| print(f"ckpt(clipskip): {ckpt_name if ckpt_name is not None else ''}") |
| else: |
| print(f"ckpt: {ckpt_name if ckpt_name is not None else ''}") |
| print(f"clip_skip: {clip_skip if clip_skip is not None else ''}") |
| if lora_name: |
| print(f"lora(mod,clip): {lora_name if lora_name is not None else ''}") |
| print(f"vae: {vae_name if vae_name is not None else ''}") |
| print(f"seed: {seed}") |
| print(f"steps: {steps}") |
| print(f"cfg: {cfg}") |
| if scheduler == "_": |
| print(f"sampler(schr): {sampler_name}") |
| else: |
| print(f"sampler: {sampler_name}") |
| print(f"scheduler: {scheduler}") |
| print(f"denoise: {denoise}") |
|
|
| if X_type == "Positive Prompt S/R" or Y_type == "Positive Prompt S/R": |
| positive_prompt = ", ".join([str(x[0]) if i == 0 else str(x[1]) for i, x in enumerate( |
| X_value)]) if X_type == "Positive Prompt S/R" else ", ".join( |
| [str(y[0]) if i == 0 else str(y[1]) for i, y in |
| enumerate(Y_value)]) if Y_type == "Positive Prompt S/R" else positive_prompt |
| print(f"+prompt_s/r: {positive_prompt}") |
|
|
| if X_type == "Negative Prompt S/R" or Y_type == "Negative Prompt S/R": |
| negative_prompt = ", ".join([str(x[0]) if i == 0 else str(x[1]) for i, x in enumerate( |
| X_value)]) if X_type == "Negative Prompt S/R" else ", ".join( |
| [str(y[0]) if i == 0 else str(y[1]) for i, y in |
| enumerate(Y_value)]) if Y_type == "Negative Prompt S/R" else negative_prompt |
| print(f"-prompt_s/r: {negative_prompt}") |
|
|
| |
| def adjusted_font_size(text, initial_font_size, latent_width): |
| font = ImageFont.truetype(str(Path(font_path)), initial_font_size) |
| text_width = font.getlength(text) |
|
|
| if text_width > (latent_width * 0.9): |
| scaling_factor = 0.9 |
| new_font_size = int(initial_font_size * (latent_width / text_width) * scaling_factor) |
| else: |
| new_font_size = initial_font_size |
|
|
| return new_font_size |
|
|
| |
| |
| |
| update_value_by_id("vae_decode", my_unique_id, False) |
|
|
| def rearrange_list_A(arr, num_cols, num_rows): |
| new_list = [] |
| for i in range(num_rows): |
| for j in range(num_cols): |
| index = j * num_rows + i |
| new_list.append(arr[index]) |
| return new_list |
|
|
| def rearrange_list_B(arr, num_rows, num_cols): |
| new_list = [] |
| for i in range(num_rows): |
| for j in range(num_cols): |
| index = i * num_cols + j |
| new_list.append(arr[index]) |
| return new_list |
|
|
| |
| num_rows = max(len(Y_value) if Y_value is not None else 0, 1) |
| num_cols = max(len(X_value) if X_value is not None else 0, 1) |
|
|
| |
| if flip_xy == True: |
| X_type, Y_type = Y_type, X_type |
| X_value, Y_value = Y_value, X_value |
| X_label, Y_label = Y_label, X_label |
| num_rows, num_cols = num_cols, num_rows |
| image_pil_list = rearrange_list_A(image_pil_list, num_rows, num_cols) |
| else: |
| image_pil_list = rearrange_list_B(image_pil_list, num_rows, num_cols) |
| image_tensor_list = rearrange_list_A(image_tensor_list, num_cols, num_rows) |
| latent_list = rearrange_list_A(latent_list, num_cols, num_rows) |
|
|
| |
| print_plot_variables(X_type, Y_type, X_value, Y_value, seed, ckpt_name, lora_params, vae_name, |
| clip_skip, steps, cfg, sampler_name, scheduler, denoise, |
| num_rows, num_cols, latent_height, latent_width) |
|
|
| |
| latent_list = torch.cat(latent_list, dim=0) |
|
|
| |
| update_value_by_id("latent", my_unique_id, latent_list) |
|
|
| |
| border_size_top = latent_width // 15 |
|
|
| |
| if len(Y_label) > 0: |
| Y_label_longest = max(len(s) for s in Y_label) |
| else: |
| |
| Y_label_longest = 0 |
|
|
| Y_label_scale = min(Y_label_longest + 4,24) / 24 |
|
|
| if Y_label_orientation == "Vertical": |
| border_size_left = border_size_top |
| else: |
| |
| border_size_left = min(latent_width, latent_height) + int(0.2 * abs(latent_width - latent_height)) |
| border_size_left = int(border_size_left * Y_label_scale) |
|
|
| |
| if Y_type == "Nothing": |
| bg_width = num_cols * latent_width + (num_cols - 1) * grid_spacing |
| x_offset_initial = 0 |
| else: |
| if Y_label_orientation == "Vertical": |
| bg_width = num_cols * latent_width + (num_cols - 1) * grid_spacing + 3 * border_size_left |
| x_offset_initial = border_size_left * 3 |
| else: |
| bg_width = num_cols * latent_width + (num_cols - 1) * grid_spacing + border_size_left |
| x_offset_initial = border_size_left |
|
|
| |
| if X_type == "Nothing": |
| bg_height = num_rows * latent_height + (num_rows - 1) * grid_spacing |
| y_offset = 0 |
| else: |
| bg_height = num_rows * latent_height + (num_rows - 1) * grid_spacing + 3 * border_size_top |
| y_offset = border_size_top * 3 |
|
|
| |
| background = Image.new('RGBA', (int(bg_width), int(bg_height)), color=(255, 255, 255, 255)) |
|
|
| for row in range(num_rows): |
|
|
| |
| x_offset = x_offset_initial |
|
|
| for col in range(num_cols): |
| |
| index = col * num_rows + row |
| img = image_pil_list[index] |
|
|
| |
| background.paste(img, (x_offset, y_offset)) |
|
|
| if row == 0 and X_type != "Nothing": |
| |
| text = X_label[col] |
|
|
| |
| initial_font_size = int(48 * img.width / 512) |
| font_size = adjusted_font_size(text, initial_font_size, img.width) |
| label_height = int(font_size*1.5) |
|
|
| |
| label_bg = Image.new('RGBA', (img.width, label_height), color=(255, 255, 255, 0)) |
| d = ImageDraw.Draw(label_bg) |
|
|
| |
| font = ImageFont.truetype(str(Path(font_path)), font_size) |
|
|
| |
| _, _, text_width, text_height = d.textbbox([0,0], text, font=font) |
| text_x = (img.width - text_width) // 2 |
| text_y = (label_height - text_height) // 2 |
|
|
| |
| d.text((text_x, text_y), text, fill='black', font=font) |
|
|
| |
| available_space = y_offset - label_height |
|
|
| |
| label_y = available_space // 2 |
|
|
| |
| background.alpha_composite(label_bg, (x_offset, label_y)) |
|
|
| if col == 0 and Y_type != "Nothing": |
| |
| text = Y_label[row] |
|
|
| |
| if Y_label_orientation == "Vertical": |
| initial_font_size = int(48 * latent_width / 512) |
| font_size = adjusted_font_size(text, initial_font_size, latent_width) |
| else: |
| initial_font_size = int(48 * (border_size_left/Y_label_scale) / 512) |
| font_size = adjusted_font_size(text, initial_font_size, int(border_size_left/Y_label_scale)) |
|
|
| |
| label_bg = Image.new('RGBA', (img.height, int(font_size*1.2)), color=(255, 255, 255, 0)) |
| d = ImageDraw.Draw(label_bg) |
|
|
| |
| font = ImageFont.truetype(str(Path(font_path)), font_size) |
|
|
| |
| _, _, text_width, text_height = d.textbbox([0,0], text, font=font) |
| text_x = (img.height - text_width) // 2 |
| text_y = (font_size - text_height) // 2 |
|
|
| |
| d.text((text_x, text_y), text, fill='black', font=font) |
|
|
| |
| if Y_label_orientation == "Vertical": |
| label_bg = label_bg.rotate(90, expand=True) |
|
|
| |
| available_space = x_offset - label_bg.width |
|
|
| |
| label_x = available_space // 2 |
|
|
| |
| if Y_label_orientation == "Vertical": |
| label_y = y_offset + (img.height - label_bg.height) // 2 |
| else: |
| label_y = y_offset + img.height - (img.height - label_bg.height) // 2 |
|
|
| |
| background.alpha_composite(label_bg, (label_x, label_y)) |
|
|
| |
| x_offset += img.width + grid_spacing |
|
|
| |
| y_offset += img.height + grid_spacing |
|
|
| images = pil2tensor(background) |
|
|
| |
| results = preview_images(images, filename_prefix) |
| update_value_by_id("results", my_unique_id, results) |
|
|
| |
| if xyplot_as_output_image == False: |
| image_tensor_list = torch.stack([tensor.squeeze() for tensor in image_tensor_list]) |
| else: |
| image_tensor_list = images |
| update_value_by_id("images", my_unique_id, image_tensor_list) |
|
|
| |
| if cache_models == "True": |
| print_loaded_objects_entries(script_node_id, prompt) |
|
|
| print("-" * 40) |
|
|
| images = list() if preview_image == "Output Only" else results |
|
|
| return { |
| "ui": {"images": images}, |
| "result": (model, positive, negative, {"samples": latent_list}, vae, image_tensor_list,) |
| } |
|
|
| |
| |
| class TSC_XYplot: |
|
|
| @classmethod |
| def INPUT_TYPES(cls): |
| return {"required": { |
| "grid_spacing": ("INT", {"default": 0, "min": 0, "max": 500, "step": 5}), |
| "XY_flip": (["False","True"],), |
| "Y_label_orientation": (["Horizontal", "Vertical"],), |
| "cache_models": (["True", "False"],), |
| "ksampler_output_image": (["Plot", "Images"],),}, |
| "optional": { |
| "dependencies": ("DEPENDENCIES", ), |
| "X": ("XY", ), |
| "Y": ("XY", ),}, |
| } |
|
|
| RETURN_TYPES = ("SCRIPT",) |
| RETURN_NAMES = ("SCRIPT",) |
| FUNCTION = "XYplot" |
| CATEGORY = "Efficiency Nodes/XY Plot" |
|
|
| def XYplot(self, grid_spacing, XY_flip, Y_label_orientation, cache_models, ksampler_output_image, dependencies=None, X=None, Y=None): |
|
|
| |
| if X != None: |
| X_type, X_value = X |
| else: |
| X_type = "Nothing" |
| X_value = [""] |
| if Y != None: |
| Y_type, Y_value = Y |
| else: |
| Y_type = "Nothing" |
| Y_value = [""] |
|
|
| |
| if (X_type == Y_type): |
| if X_type != "Nothing": |
| print(f"\033[31mXY Plot Error:\033[0m X and Y input types must be different.") |
| return (None,) |
|
|
| |
| types = ("Checkpoint", "LoRA", "Positive Prompt S/R", "Negative Prompt S/R") |
| if X_type in types or Y_type in types: |
| if dependencies == None: |
| print(f"\033[31mXY Plot Error:\033[0m The dependencies input must be connected for certain plot types.") |
| |
| return (None,) |
|
|
| |
| if X_type == "Seeds++ Batch": |
| X_value = [i for i in range(X_value[0])] |
| if Y_type == "Seeds++ Batch": |
| Y_value = [i for i in range(Y_value[0])] |
|
|
| |
| if X_type == "Sampler" and Y_type == "Scheduler": |
| |
| X_value = [(x[0], "") for x in X_value] |
| elif Y_type == "Sampler" and X_type == "Scheduler": |
| |
| Y_value = [(y[0], "") for y in Y_value] |
|
|
| |
| if X_type == "Scheduler" and Y_type != "Sampler": |
| |
| X_value = [(x, None) for x in X_value] |
|
|
| if Y_type == "Scheduler" and X_type != "Sampler": |
| |
| Y_value = [(y, None) for y in Y_value] |
|
|
| |
| if Y_type == "Checkpoint" or \ |
| Y_type == "LoRA" and X_type not in {"Checkpoint"} or \ |
| Y_type == "VAE" and X_type not in {"Checkpoint", "LoRA"} or \ |
| Y_type == "Positive Prompt S/R" and X_type not in {"Checkpoint", "LoRA", "VAE", |
| "Negative Prompt S/R"} or \ |
| Y_type == "Negative Prompt S/R" and X_type not in {"Checkpoint", "LoRA", "VAE", |
| "Positive Prompt S/R"} or \ |
| X_type == "Nothing" and Y_type != "Nothing": |
| flip_xy = True |
| X_type, Y_type = Y_type, X_type |
| X_value, Y_value = Y_value, X_value |
| else: |
| flip_xy = False |
|
|
| |
| if XY_flip == "True": |
| X_type, Y_type = Y_type, X_type |
| X_value, Y_value = Y_value, X_value |
|
|
| |
| xyplot_as_output_image = ksampler_output_image == "Plot" |
|
|
| return ((X_type, X_value, Y_type, Y_value, grid_spacing, Y_label_orientation, cache_models, |
| xyplot_as_output_image, flip_xy, dependencies),) |
|
|
|
|
| |
| class TSC_XYplot_SeedsBatch: |
|
|
| @classmethod |
| def INPUT_TYPES(cls): |
| return {"required": { |
| "batch_count": ("INT", {"default": 1, "min": 0, "max": 50}),}, |
| } |
|
|
| RETURN_TYPES = ("XY",) |
| RETURN_NAMES = ("X or Y",) |
| FUNCTION = "xy_value" |
| CATEGORY = "Efficiency Nodes/XY Plot/XY Inputs" |
|
|
| def xy_value(self, batch_count): |
| if batch_count == 0: |
| return (None,) |
| xy_type = "Seeds++ Batch" |
| xy_value = [batch_count] |
| return ((xy_type, xy_value),) |
|
|
| |
| class TSC_XYplot_Steps: |
|
|
| @classmethod |
| def INPUT_TYPES(cls): |
| return {"required": { |
| "select_count": ("INT", {"default": 0, "min": 0, "max": 5}), |
| "steps_1": ("INT", {"default": 20, "min": 1, "max": 10000}), |
| "steps_2": ("INT", {"default": 20, "min": 1, "max": 10000}), |
| "steps_3": ("INT", {"default": 20, "min": 1, "max": 10000}), |
| "steps_4": ("INT", {"default": 20, "min": 1, "max": 10000}), |
| "steps_5": ("INT", {"default": 20, "min": 1, "max": 10000}),}, |
| } |
|
|
| RETURN_TYPES = ("XY",) |
| RETURN_NAMES = ("X or Y",) |
| FUNCTION = "xy_value" |
| CATEGORY = "Efficiency Nodes/XY Plot/XY Inputs" |
|
|
| def xy_value(self, select_count, steps_1, steps_2, steps_3, steps_4, steps_5): |
| xy_type = "Steps" |
| xy_value = [step for idx, step in enumerate([steps_1, steps_2, steps_3, steps_4, steps_5], start=1) if |
| idx <= select_count] |
| if not xy_value: |
| return (None,) |
| return ((xy_type, xy_value),) |
|
|
|
|
| |
| class TSC_XYplot_CFG: |
|
|
| @classmethod |
| def INPUT_TYPES(cls): |
| return {"required": { |
| "select_count": ("INT", {"default": 0, "min": 0, "max": 5}), |
| "cfg_1": ("FLOAT", {"default": 7.0, "min": 0.0, "max": 100.0}), |
| "cfg_2": ("FLOAT", {"default": 7.0, "min": 0.0, "max": 100.0}), |
| "cfg_3": ("FLOAT", {"default": 7.0, "min": 0.0, "max": 100.0}), |
| "cfg_4": ("FLOAT", {"default": 7.0, "min": 0.0, "max": 100.0}), |
| "cfg_5": ("FLOAT", {"default": 7.0, "min": 0.0, "max": 100.0}),}, |
| } |
|
|
| RETURN_TYPES = ("XY",) |
| RETURN_NAMES = ("X or Y",) |
| FUNCTION = "xy_value" |
| CATEGORY = "Efficiency Nodes/XY Plot/XY Inputs" |
|
|
| def xy_value(self, select_count, cfg_1, cfg_2, cfg_3, cfg_4, cfg_5): |
| xy_type = "CFG Scale" |
| xy_value = [cfg for idx, cfg in enumerate([cfg_1, cfg_2, cfg_3, cfg_4, cfg_5], start=1) if idx <= select_count] |
| if not xy_value: |
| return (None,) |
| return ((xy_type, xy_value),) |
|
|
|
|
| |
| class TSC_XYplot_Sampler: |
| |
| samplers = ["None"] + comfy.samplers.KSampler.SAMPLERS |
| schedulers = ["None"] + comfy.samplers.KSampler.SCHEDULERS |
|
|
| @classmethod |
| def INPUT_TYPES(cls): |
| return {"required": { |
| "sampler_1": (cls.samplers,), |
| "scheduler_1": (cls.schedulers,), |
| "sampler_2": (cls.samplers,), |
| "scheduler_2": (cls.schedulers,), |
| "sampler_3": (cls.samplers,), |
| "scheduler_3": (cls.schedulers,), |
| "sampler_4": (cls.samplers,), |
| "scheduler_4": (cls.schedulers,), |
| "sampler_5": (cls.samplers,), |
| "scheduler_5": (cls.schedulers,),}, |
| } |
| RETURN_TYPES = ("XY",) |
| RETURN_NAMES = ("X or Y",) |
| FUNCTION = "xy_value" |
| CATEGORY = "Efficiency Nodes/XY Plot/XY Inputs" |
|
|
| def xy_value(self, sampler_1, scheduler_1, sampler_2, scheduler_2, sampler_3, scheduler_3, |
| sampler_4, scheduler_4, sampler_5, scheduler_5): |
|
|
| samplers = [sampler_1, sampler_2, sampler_3, sampler_4, sampler_5] |
| schedulers = [scheduler_1, scheduler_2, scheduler_3, scheduler_4, scheduler_5] |
|
|
| pairs = [] |
| for sampler, scheduler in zip(samplers, schedulers): |
| if sampler != "None": |
| if scheduler != "None": |
| pairs.append((sampler, scheduler)) |
| else: |
| pairs.append((sampler,None)) |
|
|
| xy_type = "Sampler" |
| xy_value = pairs |
| if not xy_value: |
| return (None,) |
| return ((xy_type, xy_value),) |
|
|
|
|
| |
| class TSC_XYplot_Scheduler: |
|
|
| schedulers = ["None"] + comfy.samplers.KSampler.SCHEDULERS |
|
|
| @classmethod |
| def INPUT_TYPES(cls): |
| return {"required": { |
| "scheduler_1": (cls.schedulers,), |
| "scheduler_2": (cls.schedulers,), |
| "scheduler_3": (cls.schedulers,), |
| "scheduler_4": (cls.schedulers,), |
| "scheduler_5": (cls.schedulers,),}, |
| } |
|
|
| RETURN_TYPES = ("XY",) |
| RETURN_NAMES = ("X or Y",) |
| FUNCTION = "xy_value" |
| CATEGORY = "Efficiency Nodes/XY Plot/XY Inputs" |
|
|
| def xy_value(self, scheduler_1, scheduler_2, scheduler_3, scheduler_4, scheduler_5): |
| xy_type = "Scheduler" |
| xy_value = [scheduler for scheduler in [scheduler_1, scheduler_2, scheduler_3, scheduler_4, scheduler_5] if |
| scheduler != "None"] |
| if not xy_value: |
| return (None,) |
| return ((xy_type, xy_value),) |
|
|
|
|
| |
| class TSC_XYplot_Denoise: |
|
|
| @classmethod |
| def INPUT_TYPES(cls): |
| return {"required": { |
| "select_count": ("INT", {"default": 0, "min": 0, "max": 5}), |
| "denoise_1": ("FLOAT", {"default": 1.0, "min": 0.00, "max": 1.0, "step": 0.01}), |
| "denoise_2": ("FLOAT", {"default": 1.0, "min": 0.00, "max": 1.0, "step": 0.01}), |
| "denoise_3": ("FLOAT", {"default": 1.0, "min": 0.00, "max": 1.0, "step": 0.01}), |
| "denoise_4": ("FLOAT", {"default": 1.0, "min": 0.00, "max": 1.0, "step": 0.01}), |
| "denoise_5": ("FLOAT", {"default": 1.0, "min": 0.00, "max": 1.0, "step": 0.01}),}, |
| } |
|
|
| RETURN_TYPES = ("XY",) |
| RETURN_NAMES = ("X or Y",) |
| FUNCTION = "xy_value" |
| CATEGORY = "Efficiency Nodes/XY Plot/XY Inputs" |
|
|
| def xy_value(self, select_count, denoise_1, denoise_2, denoise_3, denoise_4, denoise_5): |
| xy_type = "Denoise" |
| xy_value = [denoise for idx, denoise in |
| enumerate([denoise_1, denoise_2, denoise_3, denoise_4, denoise_5], start=1) if idx <= select_count] |
| if not xy_value: |
| return (None,) |
| return ((xy_type, xy_value),) |
|
|
|
|
| |
| class TSC_XYplot_VAE: |
|
|
| vaes = ["None"] + folder_paths.get_filename_list("vae") |
|
|
| @classmethod |
| def INPUT_TYPES(cls): |
| return {"required": { |
| "vae_name_1": (cls.vaes,), |
| "vae_name_2": (cls.vaes,), |
| "vae_name_3": (cls.vaes,), |
| "vae_name_4": (cls.vaes,), |
| "vae_name_5": (cls.vaes,),}, |
| } |
|
|
| RETURN_TYPES = ("XY",) |
| RETURN_NAMES = ("X or Y",) |
| FUNCTION = "xy_value" |
| CATEGORY = "Efficiency Nodes/XY Plot/XY Inputs" |
|
|
| def xy_value(self, vae_name_1, vae_name_2, vae_name_3, vae_name_4, vae_name_5): |
| xy_type = "VAE" |
| xy_value = [vae for vae in [vae_name_1, vae_name_2, vae_name_3, vae_name_4, vae_name_5] if vae != "None"] |
| if not xy_value: |
| return (None,) |
| return ((xy_type, xy_value),) |
|
|
|
|
| |
| class TSC_XYplot_PromptSR_Positive: |
|
|
| @classmethod |
| def INPUT_TYPES(cls): |
| return {"required": { |
| "search_txt": ("STRING", {"default": "", "multiline": False}), |
| "replace_count": ("INT", {"default": 0, "min": 0, "max": 4}), |
| "replace_1":("STRING", {"default": "", "multiline": False}), |
| "replace_2": ("STRING", {"default": "", "multiline": False}), |
| "replace_3": ("STRING", {"default": "", "multiline": False}), |
| "replace_4": ("STRING", {"default": "", "multiline": False}),}, |
| } |
|
|
| RETURN_TYPES = ("XY",) |
| RETURN_NAMES = ("X or Y",) |
| FUNCTION = "xy_value" |
| CATEGORY = "Efficiency Nodes/XY Plot/XY Inputs" |
|
|
| def xy_value(self, search_txt, replace_count, replace_1, replace_2, replace_3, replace_4): |
| |
| if search_txt == "": |
| return (None,) |
|
|
| xy_type = "Positive Prompt S/R" |
|
|
| |
| replacements = [replace_1, replace_2, replace_3, replace_4] |
|
|
| |
| xy_values = [(search_txt, None)] |
|
|
| if replace_count > 0: |
| |
| xy_values.extend([(search_txt, replacements[i]) for i in range(replace_count)]) |
|
|
| return ((xy_type, xy_values),) |
|
|
| |
| class TSC_XYplot_PromptSR_Negative: |
|
|
| @classmethod |
| def INPUT_TYPES(cls): |
| return {"required": { |
| "search_txt": ("STRING", {"default": "", "multiline": False}), |
| "replace_count": ("INT", {"default": 0, "min": 0, "max": 4}), |
| "replace_1":("STRING", {"default": "", "multiline": False}), |
| "replace_2": ("STRING", {"default": "", "multiline": False}), |
| "replace_3": ("STRING", {"default": "", "multiline": False}), |
| "replace_4": ("STRING", {"default": "", "multiline": False}),}, |
| } |
|
|
| RETURN_TYPES = ("XY",) |
| RETURN_NAMES = ("X or Y",) |
| FUNCTION = "xy_value" |
| CATEGORY = "Efficiency Nodes/XY Plot/XY Inputs" |
|
|
| def xy_value(self, search_txt, replace_count, replace_1, replace_2, replace_3, replace_4): |
| |
| if search_txt == "": |
| return (None,) |
|
|
| xy_type = "Negative Prompt S/R" |
|
|
| |
| replacements = [replace_1, replace_2, replace_3, replace_4] |
|
|
| |
| xy_values = [(search_txt, None)] |
|
|
| if replace_count > 0: |
| |
| xy_values.extend([(search_txt, replacements[i]) for i in range(replace_count)]) |
|
|
| return ((xy_type, xy_values),) |
|
|
| |
| class TSC_XYplot_Checkpoint: |
|
|
| checkpoints = ["None"] + folder_paths.get_filename_list("checkpoints") |
|
|
| @classmethod |
| def INPUT_TYPES(cls): |
| return {"required": { |
| "ckpt_name_1": (cls.checkpoints,), |
| "clip_skip1": ("INT", {"default": -1, "min": -24, "max": -1, "step": 1}), |
| "ckpt_name_2": (cls.checkpoints,), |
| "clip_skip2": ("INT", {"default": -1, "min": -24, "max": -1, "step": 1}), |
| "ckpt_name_3": (cls.checkpoints,), |
| "clip_skip3": ("INT", {"default": -1, "min": -24, "max": -1, "step": 1}), |
| "ckpt_name_4": (cls.checkpoints,), |
| "clip_skip4": ("INT", {"default": -1, "min": -24, "max": -1, "step": 1}), |
| "ckpt_name_5": (cls.checkpoints,), |
| "clip_skip5": ("INT", {"default": -1, "min": -24, "max": -1, "step": 1}),}, |
| } |
|
|
| RETURN_TYPES = ("XY",) |
| RETURN_NAMES = ("X or Y",) |
| FUNCTION = "xy_value" |
| CATEGORY = "Efficiency Nodes/XY Plot/XY Inputs" |
|
|
| def xy_value(self, ckpt_name_1, clip_skip1, ckpt_name_2, clip_skip2, ckpt_name_3, clip_skip3, |
| ckpt_name_4, clip_skip4, ckpt_name_5, clip_skip5): |
| xy_type = "Checkpoint" |
| checkpoints = [ckpt_name_1, ckpt_name_2, ckpt_name_3, ckpt_name_4, ckpt_name_5] |
| clip_skips = [clip_skip1, clip_skip2, clip_skip3, clip_skip4, clip_skip5] |
| xy_value = [(checkpoint, clip_skip) for checkpoint, clip_skip in zip(checkpoints, clip_skips) if |
| checkpoint != "None"] |
| if not xy_value: |
| return (None,) |
| return ((xy_type, xy_value),) |
|
|
| |
| class TSC_XYplot_ClipSkip: |
|
|
| @classmethod |
| def INPUT_TYPES(cls): |
| return {"required": { |
| "select_count": ("INT", {"default": 0, "min": 0, "max": 5}), |
| "clip_skip_1": ("INT", {"default": -1, "min": -24, "max": -1, "step": 1}), |
| "clip_skip_2": ("INT", {"default": -2, "min": -24, "max": -1, "step": 1}), |
| "clip_skip_3": ("INT", {"default": -3, "min": -24, "max": -1, "step": 1}), |
| "clip_skip_4": ("INT", {"default": -4, "min": -24, "max": -1, "step": 1}), |
| "clip_skip_5": ("INT", {"default": -5, "min": -24, "max": -1, "step": 1}),}, |
| } |
|
|
| RETURN_TYPES = ("XY",) |
| RETURN_NAMES = ("X or Y",) |
| FUNCTION = "xy_value" |
| CATEGORY = "Efficiency Nodes/XY Plot/XY Inputs" |
|
|
| def xy_value(self, select_count, clip_skip_1, clip_skip_2, clip_skip_3, clip_skip_4, clip_skip_5): |
| xy_type = "Clip Skip" |
| xy_value = [clip_skip for idx, clip_skip in |
| enumerate([clip_skip_1, clip_skip_2, clip_skip_3, clip_skip_4, clip_skip_5], start=1) if idx <= select_count] |
| if not xy_value: |
| return (None,) |
| return ((xy_type, xy_value),) |
|
|
| |
| class TSC_XYplot_LoRA: |
|
|
| loras = ["None"] + folder_paths.get_filename_list("loras") |
|
|
| @classmethod |
| def INPUT_TYPES(cls): |
| return {"required": { |
| "model_strengths": ("FLOAT", {"default": 1.0, "min": -10.0, "max": 10.0, "step": 0.01}), |
| "clip_strengths": ("FLOAT", {"default": 1.0, "min": -10.0, "max": 10.0, "step": 0.01}), |
| "lora_name_1": (cls.loras,), |
| "lora_name_2": (cls.loras,), |
| "lora_name_3": (cls.loras,), |
| "lora_name_4": (cls.loras,), |
| "lora_name_5": (cls.loras,)}, |
| "optional": {"lora_stack": ("LORA_STACK", )} |
| } |
|
|
| RETURN_TYPES = ("XY",) |
| RETURN_NAMES = ("X or Y",) |
| FUNCTION = "xy_value" |
| CATEGORY = "Efficiency Nodes/XY Plot/XY Inputs" |
|
|
| def xy_value(self, model_strengths, clip_strengths, lora_name_1, lora_name_2, lora_name_3, lora_name_4, lora_name_5, |
| lora_stack=None): |
| xy_type = "LoRA" |
| loras = [lora_name_1, lora_name_2, lora_name_3, lora_name_4, lora_name_5] |
|
|
| |
| xy_value = [[(lora, model_strengths, clip_strengths)] + (lora_stack if lora_stack else []) for lora in loras if |
| lora != "None"] |
|
|
| if not xy_value: |
| return (None,) |
| return ((xy_type, xy_value),) |
|
|
|
|
| |
| class TSC_XYplot_LoRA_Adv: |
|
|
| loras = ["None"] + folder_paths.get_filename_list("loras") |
|
|
| @classmethod |
| def INPUT_TYPES(cls): |
| return {"required": { |
| "lora_name_1": (cls.loras,), |
| "model_str_1": ("FLOAT", {"default": 1.0, "min": -10.0, "max": 10.0, "step": 0.01}), |
| "clip_str_1": ("FLOAT", {"default": 1.0, "min": -10.0, "max": 10.0, "step": 0.01}), |
| "lora_name_2": (cls.loras,), |
| "model_str_2": ("FLOAT", {"default": 1.0, "min": -10.0, "max": 10.0, "step": 0.01}), |
| "clip_str_2": ("FLOAT", {"default": 1.0, "min": -10.0, "max": 10.0, "step": 0.01}), |
| "lora_name_3": (cls.loras,), |
| "model_str_3": ("FLOAT", {"default": 1.0, "min": -10.0, "max": 10.0, "step": 0.01}), |
| "clip_str_3": ("FLOAT", {"default": 1.0, "min": -10.0, "max": 10.0, "step": 0.01}), |
| "lora_name_4": (cls.loras,), |
| "model_str_4": ("FLOAT", {"default": 1.0, "min": -10.0, "max": 10.0, "step": 0.01}), |
| "clip_str_4": ("FLOAT", {"default": 1.0, "min": -10.0, "max": 10.0, "step": 0.01}), |
| "lora_name_5": (cls.loras,), |
| "model_str_5": ("FLOAT", {"default": 1.0, "min": -10.0, "max": 10.0, "step": 0.01}), |
| "clip_str_5": ("FLOAT", {"default": 1.0, "min": -10.0, "max": 10.0, "step": 0.01}),}, |
| "optional": {"lora_stack": ("LORA_STACK",)} |
| } |
|
|
| RETURN_TYPES = ("XY",) |
| RETURN_NAMES = ("X or Y",) |
| FUNCTION = "xy_value" |
| CATEGORY = "Efficiency Nodes/XY Plot/XY Inputs" |
|
|
| def xy_value(self, lora_name_1, model_str_1, clip_str_1, lora_name_2, model_str_2, clip_str_2, lora_name_3, |
| model_str_3, |
| clip_str_3, lora_name_4, model_str_4, clip_str_4, lora_name_5, model_str_5, clip_str_5, |
| lora_stack=None): |
| xy_type = "LoRA" |
| loras = [lora_name_1, lora_name_2, lora_name_3, lora_name_4, lora_name_5] |
| model_strs = [model_str_1, model_str_2, model_str_3, model_str_4, model_str_5] |
| clip_strs = [clip_str_1, clip_str_2, clip_str_3, clip_str_4, clip_str_5] |
|
|
| |
| xy_value = [[(lora, model_str, clip_str)] + (lora_stack if lora_stack else []) for lora, model_str, clip_str in |
| zip(loras, model_strs, clip_strs) if lora != "None"] |
|
|
| if not xy_value: |
| return (None,) |
| return ((xy_type, xy_value),) |
|
|
|
|
| |
| class TSC_XYplot_LoRA_Stacks: |
|
|
| @classmethod |
| def INPUT_TYPES(cls): |
| return {"required": { |
| "node_state": (["Enabled", "Disabled"],)}, |
| "optional": { |
| "lora_stack_1": ("LORA_STACK",), |
| "lora_stack_2": ("LORA_STACK",), |
| "lora_stack_3": ("LORA_STACK",), |
| "lora_stack_4": ("LORA_STACK",), |
| "lora_stack_5": ("LORA_STACK",),}, |
| } |
|
|
| RETURN_TYPES = ("XY",) |
| RETURN_NAMES = ("X or Y",) |
| FUNCTION = "xy_value" |
| CATEGORY = "Efficiency Nodes/XY Plot/XY Inputs" |
|
|
| def xy_value(self, node_state, lora_stack_1=None, lora_stack_2=None, lora_stack_3=None, lora_stack_4=None, lora_stack_5=None): |
| xy_type = "LoRA" |
| xy_value = [stack for stack in [lora_stack_1, lora_stack_2, lora_stack_3, lora_stack_4, lora_stack_5] if stack is not None] |
| if not xy_value or not any(xy_value) or node_state == "Disabled": |
| return (None,) |
| else: |
| return ((xy_type, xy_value),) |
|
|
| |
| class TSC_XYplot_Manual_XY_Entry_Info: |
|
|
| syntax = "(X/Y_types) (X/Y_values)\n" \ |
| "Seeds++ Batch batch_count\n" \ |
| "Steps steps_1;steps_2;...\n" \ |
| "CFG Scale cfg_1;cfg_2;...\n" \ |
| "Sampler(1) sampler_1;sampler_2;...\n" \ |
| "Sampler(2) sampler_1,scheduler_1;...\n" \ |
| "Sampler(3) sampler_1;...;,default_scheduler\n" \ |
| "Scheduler scheduler_1;scheduler_2;...\n" \ |
| "Denoise denoise_1;denoise_2;...\n" \ |
| "VAE vae_1;vae_2;vae_3;...\n" \ |
| "+Prompt S/R search_txt;replace_1;replace_2;...\n" \ |
| "-Prompt S/R search_txt;replace_1;replace_2;...\n" \ |
| "Checkpoint(1) ckpt_1;ckpt_2;ckpt_3;...\n" \ |
| "Checkpoint(2) ckpt_1,clip_skip_1;...\n" \ |
| "Checkpoint(3) ckpt_1;ckpt_2;...;,default_clip_skip\n" \ |
| "Clip Skip clip_skip_1;clip_skip_2;...\n" \ |
| "LoRA(1) lora_1;lora_2;lora_3;...\n" \ |
| "LoRA(2) lora_1;...;,default_model_str,default_clip_str\n" \ |
| "LoRA(3) lora_1,model_str_1,clip_str_1;..." |
|
|
| samplers = ";\n".join(comfy.samplers.KSampler.SAMPLERS) |
| schedulers = ";\n".join(comfy.samplers.KSampler.SCHEDULERS) |
| vaes = ";\n".join(folder_paths.get_filename_list("vae")) |
| ckpts = ";\n".join(folder_paths.get_filename_list("checkpoints")) |
| loras = ";\n".join(folder_paths.get_filename_list("loras")) |
|
|
| @classmethod |
| def INPUT_TYPES(cls): |
| return {"required": { |
| "notes": ("STRING", {"default": |
| f"_____________SYNTAX_____________\n{cls.syntax}\n\n" |
| f"____________SAMPLERS____________\n{cls.samplers}\n\n" |
| f"___________SCHEDULERS___________\n{cls.schedulers}\n\n" |
| f"_____________VAES_______________\n{cls.vaes}\n\n" |
| f"___________CHECKPOINTS__________\n{cls.ckpts}\n\n" |
| f"_____________LORAS______________\n{cls.loras}\n","multiline": True}),},} |
|
|
| RETURN_TYPES = () |
| CATEGORY = "Efficiency Nodes/XY Plot/XY Inputs" |
|
|
| |
| class TSC_XYplot_Manual_XY_Entry: |
|
|
| @classmethod |
| def INPUT_TYPES(cls): |
| return {"required": { |
| "X_type": (["Nothing", "Seeds++ Batch", "Steps", "CFG Scale", "Sampler", "Scheduler", "Denoise", "VAE", |
| "Positive Prompt S/R", "Negative Prompt S/R", "Checkpoint", "Clip Skip", "LoRA"],), |
| "X_value": ("STRING", {"default": "", "multiline": True}), |
| "Y_type": (["Nothing", "Seeds++ Batch", "Steps", "CFG Scale", "Sampler", "Scheduler", "Denoise", "VAE", |
| "Positive Prompt S/R", "Negative Prompt S/R", "Checkpoint", "Clip Skip", "LoRA"],), |
| "Y_value": ("STRING", {"default": "", "multiline": True}),},} |
|
|
| RETURN_TYPES = ("XY", "XY",) |
| RETURN_NAMES = ("X", "Y",) |
| FUNCTION = "xy_value" |
| CATEGORY = "Efficiency Nodes/XY Plot/XY Inputs" |
|
|
| def xy_value(self, X_type, X_value, Y_type, Y_value, prompt=None, my_unique_id=None): |
|
|
| |
| if X_type not in {"Positive Prompt S/R", "Negative Prompt S/R", "VAE", "Checkpoint", "LoRA"}: |
| X_value = X_value.replace(" ", "") |
| X_value = X_value.replace("\n", "") |
| X_value = X_value.rstrip(";") |
| X_value = X_value.split(";") |
|
|
| |
| if Y_type not in {"Positive Prompt S/R", "Negative Prompt S/R", "VAE", "Checkpoint", "LoRA"}: |
| Y_value = Y_value.replace(" ", "") |
| Y_value = Y_value.replace("\n", "") |
| Y_value = Y_value.rstrip(";") |
| Y_value = Y_value.split(";") |
|
|
| |
| bounds = { |
| "Seeds++ Batch": {"min": 0, "max": 50}, |
| "Steps": {"min": 1, "max": 10000}, |
| "CFG Scale": {"min": 0, "max": 100}, |
| "Sampler": {"options": comfy.samplers.KSampler.SAMPLERS}, |
| "Scheduler": {"options": comfy.samplers.KSampler.SCHEDULERS}, |
| "Denoise": {"min": 0, "max": 1}, |
| "VAE": {"options": folder_paths.get_filename_list("vae")}, |
| "Checkpoint": {"options": folder_paths.get_filename_list("checkpoints")}, |
| "Clip Skip": {"min": -24, "max": -1}, |
| "LoRA": {"options": folder_paths.get_filename_list("loras"), |
| "model_str": {"min": 0, "max": 10},"clip_str": {"min": 0, "max": 10},}, |
| } |
|
|
| |
| def validate_value(value, value_type, bounds): |
| |
| |
| if value_type == "Seeds++ Batch": |
| try: |
| x = int(float(value)) |
| if x < bounds["Seeds++ Batch"]["min"]: |
| x = bounds["Seeds++ Batch"]["min"] |
| elif x > bounds["Seeds++ Batch"]["max"]: |
| x = bounds["Seeds++ Batch"]["max"] |
| except ValueError: |
| print(f"\033[31mXY Plot Error:\033[0m '{value}' is not a valid batch count.") |
| return None |
| if float(value) != x: |
| print(f"\033[31mmXY Plot Error:\033[0m '{value}' is not a valid batch count.") |
| return None |
| return x |
| |
| |
| elif value_type == "Steps": |
| try: |
| x = int(value) |
| if x < bounds["Steps"]["min"]: |
| x = bounds["Steps"]["min"] |
| elif x > bounds["Steps"]["max"]: |
| x = bounds["Steps"]["max"] |
| return x |
| except ValueError: |
| print( |
| f"\033[31mXY Plot Error:\033[0m '{value}' is not a valid Step count.") |
| return None |
| |
| |
| elif value_type == "CFG Scale": |
| try: |
| x = float(value) |
| if x < bounds["CFG Scale"]["min"]: |
| x = bounds["CFG Scale"]["min"] |
| elif x > bounds["CFG Scale"]["max"]: |
| x = bounds["CFG Scale"]["max"] |
| return x |
| except ValueError: |
| print( |
| f"\033[31mXY Plot Error:\033[0m '{value}' is not a number between {bounds['CFG Scale']['min']}" |
| f" and {bounds['CFG Scale']['max']} for CFG Scale.") |
| return None |
| |
| |
| elif value_type == "Sampler": |
| if isinstance(value, str) and ',' in value: |
| value = tuple(map(str.strip, value.split(','))) |
| if isinstance(value, tuple): |
| if len(value) >= 2: |
| value = value[:2] |
| sampler, scheduler = value |
| scheduler = scheduler.lower() |
| if sampler not in bounds["Sampler"]["options"]: |
| valid_samplers = '\n'.join(bounds["Sampler"]["options"]) |
| print( |
| f"\033[31mXY Plot Error:\033[0m '{sampler}' is not a valid sampler. Valid samplers are:\n{valid_samplers}") |
| sampler = None |
| if scheduler not in bounds["Scheduler"]["options"]: |
| valid_schedulers = '\n'.join(bounds["Scheduler"]["options"]) |
| print( |
| f"\033[31mXY Plot Error:\033[0m '{scheduler}' is not a valid scheduler. Valid schedulers are:\n{valid_schedulers}") |
| scheduler = None |
| if sampler is None or scheduler is None: |
| return None |
| else: |
| return sampler, scheduler |
| else: |
| print( |
| f"\033[31mXY Plot Error:\033[0m '{value}' is not a valid sampler.'") |
| return None |
| else: |
| if value not in bounds["Sampler"]["options"]: |
| valid_samplers = '\n'.join(bounds["Sampler"]["options"]) |
| print( |
| f"\033[31mXY Plot Error:\033[0m '{value}' is not a valid sampler. Valid samplers are:\n{valid_samplers}") |
| return None |
| else: |
| return value, None |
| |
| |
| elif value_type == "Scheduler": |
| if value not in bounds["Scheduler"]["options"]: |
| valid_schedulers = '\n'.join(bounds["Scheduler"]["options"]) |
| print( |
| f"\033[31mXY Plot Error:\033[0m '{value}' is not a valid Scheduler. Valid Schedulers are:\n{valid_schedulers}") |
| return None |
| else: |
| return value |
| |
| |
| elif value_type == "Denoise": |
| try: |
| x = float(value) |
| if x < bounds["Denoise"]["min"]: |
| x = bounds["Denoise"]["min"] |
| elif x > bounds["Denoise"]["max"]: |
| x = bounds["Denoise"]["max"] |
| return x |
| except ValueError: |
| print( |
| f"\033[31mXY Plot Error:\033[0m '{value}' is not a number between {bounds['Denoise']['min']} " |
| f"and {bounds['Denoise']['max']} for Denoise.") |
| return None |
| |
| |
| elif value_type == "VAE": |
| if value not in bounds["VAE"]["options"]: |
| valid_vaes = '\n'.join(bounds["VAE"]["options"]) |
| print(f"\033[31mXY Plot Error:\033[0m '{value}' is not a valid VAE. Valid VAEs are:\n{valid_vaes}") |
| return None |
| else: |
| return value |
| |
| |
| elif value_type == "Checkpoint": |
| if isinstance(value, str) and ',' in value: |
| value = tuple(map(str.strip, value.split(','))) |
| if isinstance(value, tuple): |
| if len(value) >= 2: |
| value = value[:2] |
| checkpoint, clip_skip = value |
| try: |
| clip_skip = int(clip_skip) |
| except ValueError: |
| print(f"\033[31mXY Plot Error:\033[0m '{clip_skip}' is not a valid clip_skip. " |
| f"Valid clip skip values are integers between {bounds['Clip Skip']['min']} and {bounds['Clip Skip']['max']}.") |
| return None |
| if checkpoint not in bounds["Checkpoint"]["options"]: |
| valid_checkpoints = '\n'.join(bounds["Checkpoint"]["options"]) |
| print( |
| f"\033[31mXY Plot Error:\033[0m '{checkpoint}' is not a valid checkpoint. Valid checkpoints are:\n{valid_checkpoints}") |
| checkpoint = None |
| if clip_skip < bounds["Clip Skip"]["min"] or clip_skip > bounds["Clip Skip"]["max"]: |
| print(f"\033[31mXY Plot Error:\033[0m '{clip_skip}' is not a valid clip skip. " |
| f"Valid clip skip values are integers between {bounds['Clip Skip']['min']} and {bounds['Clip Skip']['max']}.") |
| clip_skip = None |
| if checkpoint is None or clip_skip is None: |
| return None |
| else: |
| return checkpoint, clip_skip |
| else: |
| print( |
| f"\033[31mXY Plot Error:\033[0m '{value}' is not a valid checkpoint.'") |
| return None |
| else: |
| if value not in bounds["Checkpoint"]["options"]: |
| valid_checkpoints = '\n'.join(bounds["Checkpoint"]["options"]) |
| print( |
| f"\033[31mXY Plot Error:\033[0m '{value}' is not a valid checkpoint. Valid checkpoints are:\n{valid_checkpoints}") |
| return None |
| else: |
| return value, None |
| |
| |
| elif value_type == "Clip Skip": |
| try: |
| x = int(value) |
| if x < bounds["Clip Skip"]["min"]: |
| x = bounds["Clip Skip"]["min"] |
| elif x > bounds["Clip Skip"]["max"]: |
| x = bounds["Clip Skip"]["max"] |
| return x |
| except ValueError: |
| print(f"\033[31mXY Plot Error:\033[0m '{value}' is not a valid Clip Skip.") |
| return None |
| |
| |
| elif value_type == "LoRA": |
| if isinstance(value, str) and ',' in value: |
| value = tuple(map(str.strip, value.split(','))) |
|
|
| if isinstance(value, tuple): |
| lora_name, model_str, clip_str = (value + (1.0, 1.0))[:3] |
|
|
| if lora_name not in bounds["LoRA"]["options"]: |
| valid_loras = '\n'.join(bounds["LoRA"]["options"]) |
| print(f"\033[31mXY Plot Error:\033[0m '{lora_name}' is not a valid LoRA. Valid LoRAs are:\n{valid_loras}") |
| lora_name = None |
|
|
| try: |
| model_str = float(model_str) |
| clip_str = float(clip_str) |
| except ValueError: |
| print(f"\033[31mXY Plot Error:\033[0m The LoRA model strength and clip strength values should be numbers" |
| f" between {bounds['LoRA']['model_str']['min']} and {bounds['LoRA']['model_str']['max']}.") |
| return None |
|
|
| if model_str < bounds["LoRA"]["model_str"]["min"] or model_str > bounds["LoRA"]["model_str"]["max"]: |
| print(f"\033[31mXY Plot Error:\033[0m '{model_str}' is not a valid LoRA model strength value. " |
| f"Valid lora model strength values are between {bounds['LoRA']['model_str']['min']} and {bounds['LoRA']['model_str']['max']}.") |
| model_str = None |
|
|
| if clip_str < bounds["LoRA"]["clip_str"]["min"] or clip_str > bounds["LoRA"]["clip_str"]["max"]: |
| print(f"\033[31mXY Plot Error:\033[0m '{clip_str}' is not a valid LoRA clip strength value. " |
| f"Valid lora clip strength values are between {bounds['LoRA']['clip_str']['min']} and {bounds['LoRA']['clip_str']['max']}.") |
| clip_str = None |
|
|
| if lora_name is None or model_str is None or clip_str is None: |
| return None |
| else: |
| return lora_name, model_str, clip_str |
| else: |
| if value not in bounds["LoRA"]["options"]: |
| valid_loras = '\n'.join(bounds["LoRA"]["options"]) |
| print( |
| f"\033[31mXY Plot Error:\033[0m '{value}' is not a valid LoRA. Valid LoRAs are:\n{valid_loras}") |
| return None |
| else: |
| return value, 1.0, 1.0 |
|
|
| |
| else: |
| return None |
|
|
| |
| if len(X_value) != 1 and X_type == "Seeds++ Batch": |
| print(f"\033[31mXY Plot Error:\033[0m '{';'.join(X_value)}' is not a valid batch count.") |
| return (None,None,) |
|
|
| |
| if len(Y_value) != 1 and Y_type == "Seeds++ Batch": |
| print(f"\033[31mXY Plot Error:\033[0m '{';'.join(Y_value)}' is not a valid batch count.") |
| return (None,None,) |
|
|
| |
| if X_type in ["Sampler", "Checkpoint", "LoRA"]: |
| if X_value[-1].startswith(','): |
| |
| suffixes = X_value.pop().lstrip(',').split(',') |
| |
| X_value = [entry.split(',') for entry in X_value] |
| |
| for entry in X_value: |
| entry += suffixes[len(entry) - 1:] |
| |
| X_value = [','.join(entry) for entry in X_value] |
|
|
| |
| if Y_type in ["Sampler", "Checkpoint", "LoRA"]: |
| if Y_value[-1].startswith(','): |
| |
| suffixes = Y_value.pop().lstrip(',').split(',') |
| |
| Y_value = [entry.split(',') for entry in Y_value] |
| |
| for entry in Y_value: |
| entry += suffixes[len(entry) - 1:] |
| |
| Y_value = [','.join(entry) for entry in Y_value] |
|
|
| |
| if X_type in {"Positive Prompt S/R", "Negative Prompt S/R"}: |
| if X_value[0] == '': |
| print(f"\033[31mXY Plot Error:\033[0m Prompt S/R value can not be empty.") |
| return (None, None,) |
| else: |
| X_value = [(X_value[0], None) if i == 0 else (X_value[0], x) for i, x in enumerate(X_value)] |
|
|
| |
| if Y_type in {"Positive Prompt S/R", "Negative Prompt S/R"}: |
| if Y_value[0] == '': |
| print(f"\033[31mXY Plot Error:\033[0m Prompt S/R value can not be empty.") |
| return (None, None,) |
| else: |
| Y_value = [(Y_value[0], None) if i == 0 else (Y_value[0], y) for i, y in enumerate(Y_value)] |
|
|
| |
| if X_type not in {"Nothing", "Positive Prompt S/R", "Negative Prompt S/R"}: |
| for i in range(len(X_value)): |
| X_value[i] = validate_value(X_value[i], X_type, bounds) |
| if X_value[i] == None: |
| return (None,None,) |
|
|
| |
| if Y_type not in {"Nothing", "Positive Prompt S/R", "Negative Prompt S/R"}: |
| for i in range(len(Y_value)): |
| Y_value[i] = validate_value(Y_value[i], Y_type, bounds) |
| if Y_value[i] == None: |
| return (None,None,) |
|
|
| |
| if X_type == "LoRA": |
| X_value = [X_value] |
| if Y_type == "LoRA": |
| Y_value = [Y_value] |
|
|
| |
| if X_type == "Nothing": |
| X_value = [""] |
| if Y_type == "Nothing": |
| Y_value = [""] |
|
|
| return ((X_type, X_value), (Y_type, Y_value),) |
|
|
| |
| class TSC_XYplot_JoinInputs: |
|
|
| @classmethod |
| def INPUT_TYPES(cls): |
| return {"required": { |
| "XY_1": ("XY",), |
| "XY_2": ("XY",),}, |
| } |
|
|
| RETURN_TYPES = ("XY",) |
| RETURN_NAMES = ("X or Y",) |
| FUNCTION = "xy_value" |
| CATEGORY = "Efficiency Nodes/XY Plot/XY Inputs" |
|
|
| def xy_value(self, XY_1, XY_2): |
| xy_type_1, xy_value_1 = XY_1 |
| xy_type_2, xy_value_2 = XY_2 |
|
|
| if xy_type_1 != xy_type_2: |
| print(f"\033[31mJoin XY Inputs Error:\033[0m Input types must match") |
| return (None,) |
| elif xy_type_1 == "Seeds++ Batch": |
| xy_type = xy_type_1 |
| xy_value = [xy_value_1[0] + xy_value_2[0]] |
| elif xy_type_1 == "Positive Prompt S/R" or xy_type_1 == "Negative Prompt S/R": |
| xy_type = xy_type_1 |
| xy_value = xy_value_1 + [(xy_value_1[0][0], t[1]) for t in xy_value_2[1:]] |
| else: |
| xy_type = xy_type_1 |
| xy_value = xy_value_1 + xy_value_2 |
| return ((xy_type, xy_value),) |
|
|
| |
| |
| class TSC_ImageOverlay: |
|
|
| @classmethod |
| def INPUT_TYPES(cls): |
| return { |
| "required": { |
| "base_image": ("IMAGE",), |
| "overlay_image": ("IMAGE",), |
| "overlay_resize": (["None", "Fit", "Resize by rescale_factor", "Resize to width & heigth"],), |
| "resize_method": (["nearest-exact", "bilinear", "area"],), |
| "rescale_factor": ("FLOAT", {"default": 1, "min": 0.01, "max": 16.0, "step": 0.1}), |
| "width": ("INT", {"default": 512, "min": 0, "max": MAX_RESOLUTION, "step": 64}), |
| "height": ("INT", {"default": 512, "min": 0, "max": MAX_RESOLUTION, "step": 64}), |
| "x_offset": ("INT", {"default": 0, "min": -48000, "max": 48000, "step": 10}), |
| "y_offset": ("INT", {"default": 0, "min": -48000, "max": 48000, "step": 10}), |
| "rotation": ("INT", {"default": 0, "min": -180, "max": 180, "step": 5}), |
| "opacity": ("FLOAT", {"default": 0, "min": 0, "max": 100, "step": 5}), |
| }, |
| "optional": {"optional_mask": ("MASK",),} |
| } |
|
|
| RETURN_TYPES = ("IMAGE",) |
| FUNCTION = "apply_overlay_image" |
| CATEGORY = "Efficiency Nodes/Image" |
|
|
| def apply_overlay_image(self, base_image, overlay_image, overlay_resize, resize_method, rescale_factor, |
| width, height, x_offset, y_offset, rotation, opacity, optional_mask=None): |
|
|
| |
| size = width, height |
| location = x_offset, y_offset |
| mask = optional_mask |
|
|
| |
| if overlay_resize != "None": |
| |
| overlay_image_size = overlay_image.size() |
| overlay_image_size = (overlay_image_size[2], overlay_image_size[1]) |
| if overlay_resize == "Fit": |
| overlay_image_size = (base_image.size[0],base_image.size[1]) |
| elif overlay_resize == "Resize by rescale_factor": |
| overlay_image_size = tuple(int(dimension * rescale_factor) for dimension in overlay_image_size) |
| elif overlay_resize == "Resize to width & heigth": |
| overlay_image_size = (size[0], size[1]) |
|
|
| samples = overlay_image.movedim(-1, 1) |
| overlay_image = comfy.utils.common_upscale(samples, overlay_image_size[0], overlay_image_size[1], resize_method, False) |
| overlay_image = overlay_image.movedim(1, -1) |
| |
| overlay_image = tensor2pil(overlay_image) |
|
|
| |
| overlay_image = overlay_image.convert('RGBA') |
| overlay_image.putalpha(Image.new("L", overlay_image.size, 255)) |
|
|
| |
| if mask is not None: |
| |
| mask = tensor2pil(mask) |
| mask = mask.resize(overlay_image.size) |
| |
| overlay_image.putalpha(ImageOps.invert(mask)) |
|
|
| |
| overlay_image = overlay_image.rotate(rotation, expand=True) |
|
|
| |
| r, g, b, a = overlay_image.split() |
| a = a.point(lambda x: max(0, int(x * (1 - opacity / 100)))) |
| overlay_image.putalpha(a) |
|
|
| |
| base_image_list = torch.unbind(base_image, dim=0) |
|
|
| |
| processed_base_image_list = [] |
| for tensor in base_image_list: |
| |
| image = tensor2pil(tensor) |
|
|
| |
| if mask is None: |
| image.paste(overlay_image, location) |
| else: |
| image.paste(overlay_image, location, overlay_image) |
|
|
| |
| processed_tensor = pil2tensor(image) |
|
|
| |
| processed_base_image_list.append(processed_tensor) |
|
|
| |
| base_image = torch.stack([tensor.squeeze() for tensor in processed_base_image_list]) |
|
|
| |
| return (base_image,) |
|
|
| |
| |
| def install_simpleeval(): |
| if 'simpleeval' not in packages(): |
| print("\033[32mEfficiency Nodes:\033[0m") |
| subprocess.check_call([sys.executable, '-m', 'pip', 'install', 'simpleeval']) |
|
|
| def packages(versions=False): |
| return [(r.decode().split('==')[0] if not versions else r.decode()) for r in subprocess.check_output([sys.executable, '-m', 'pip', 'freeze']).split()] |
|
|
| install_simpleeval() |
| from simpleeval import simple_eval |
|
|
| |
| class TSC_EvaluateInts: |
| @classmethod |
| def INPUT_TYPES(cls): |
| return {"required": { |
| "python_expression": ("STRING", {"default": "((a + b) - c) / 2", "multiline": False}), |
| "print_to_console": (["False", "True"],),}, |
| "optional": { |
| "a": ("INT", {"default": 0, "min": -48000, "max": 48000, "step": 1}), |
| "b": ("INT", {"default": 0, "min": -48000, "max": 48000, "step": 1}), |
| "c": ("INT", {"default": 0, "min": -48000, "max": 48000, "step": 1}),}, |
| } |
| RETURN_TYPES = ("INT", "FLOAT", "STRING",) |
| OUTPUT_NODE = True |
| FUNCTION = "evaluate" |
| CATEGORY = "Efficiency Nodes/Simple Eval" |
|
|
| def evaluate(self, python_expression, print_to_console, a=0, b=0, c=0): |
| |
| result = simple_eval(python_expression, names={'a': a, 'b': b, 'c': c}) |
| int_result = int(result) |
| float_result = float(result) |
| string_result = str(result) |
| if print_to_console == "True": |
| print("\n\033[31mEvaluate Integers:\033[0m") |
| print(f"\033[90m{{a = {a} , b = {b} , c = {c}}} \033[0m") |
| print(f"{python_expression} = \033[92m INT: " + str(int_result) + " , FLOAT: " + str( |
| float_result) + ", STRING: " + string_result + "\033[0m") |
| return (int_result, float_result, string_result,) |
|
|
| |
| class TSC_EvaluateFloats: |
| @classmethod |
| def INPUT_TYPES(cls): |
| return {"required": { |
| "python_expression": ("STRING", {"default": "((a + b) - c) / 2", "multiline": False}), |
| "print_to_console": (["False", "True"],),}, |
| "optional": { |
| "a": ("FLOAT", {"default": 0, "min": -sys.float_info.max, "max": sys.float_info.max, "step": 1}), |
| "b": ("FLOAT", {"default": 0, "min": -sys.float_info.max, "max": sys.float_info.max, "step": 1}), |
| "c": ("FLOAT", {"default": 0, "min": -sys.float_info.max, "max": sys.float_info.max, "step": 1}),}, |
| } |
| RETURN_TYPES = ("INT", "FLOAT", "STRING",) |
| OUTPUT_NODE = True |
| FUNCTION = "evaluate" |
| CATEGORY = "Efficiency Nodes/Simple Eval" |
|
|
| def evaluate(self, python_expression, print_to_console, a=0, b=0, c=0): |
| |
| result = simple_eval(python_expression, names={'a': a, 'b': b, 'c': c}) |
| int_result = int(result) |
| float_result = float(result) |
| string_result = str(result) |
| if print_to_console == "True": |
| print("\n\033[31mEvaluate Floats:\033[0m") |
| print(f"\033[90m{{a = {a} , b = {b} , c = {c}}} \033[0m") |
| print(f"{python_expression} = \033[92m INT: " + str(int_result) + " , FLOAT: " + str( |
| float_result) + ", STRING: " + string_result + "\033[0m") |
| return (int_result, float_result, string_result,) |
|
|
| |
| class TSC_EvaluateStrs: |
| @classmethod |
| def INPUT_TYPES(cls): |
| return {"required": { |
| "python_expression": ("STRING", {"default": "a + b + c", "multiline": False}), |
| "print_to_console": (["False", "True"],)}, |
| "optional": { |
| "a": ("STRING", {"default": "Hello", "multiline": False}), |
| "b": ("STRING", {"default": " World", "multiline": False}), |
| "c": ("STRING", {"default": "!", "multiline": False}),} |
| } |
| RETURN_TYPES = ("STRING",) |
| OUTPUT_NODE = True |
| FUNCTION = "evaluate" |
| CATEGORY = "Efficiency Nodes/Simple Eval" |
|
|
| def evaluate(self, python_expression, print_to_console, a="", b="", c=""): |
| variables = {'a': a, 'b': b, 'c': c} |
| functions = {"len": len} |
| result = simple_eval(python_expression, names=variables, functions=functions) |
| if print_to_console == "True": |
| print("\n\033[31mEvaluate Strings:\033[0m") |
| print(f"\033[90ma = {a} \nb = {b} \nc = {c}\033[0m") |
| print(f"{python_expression} = \033[92m" + str(result) + "\033[0m") |
| return (str(result),) |
|
|
| |
| class TSC_EvalExamples: |
| filepath = os.path.join(my_dir, 'workflows', 'SimpleEval_Node_Examples.txt') |
| with open(filepath, 'r') as file: |
| examples = file.read() |
| @classmethod |
| def INPUT_TYPES(cls): |
| return {"required": { "models_text": ("STRING", {"default": cls.examples ,"multiline": True}),},} |
| RETURN_TYPES = () |
| CATEGORY = "Efficiency Nodes/Simple Eval" |
|
|
| |
| NODE_CLASS_MAPPINGS = { |
| "KSampler (Efficient)": TSC_KSampler, |
| "Efficient Loader": TSC_EfficientLoader, |
| "LoRA Stacker": TSC_LoRA_Stacker, |
| "LoRA Stacker Adv.": TSC_LoRA_Stacker_Adv, |
| "XY Plot": TSC_XYplot, |
| "XY Input: Seeds++ Batch": TSC_XYplot_SeedsBatch, |
| "XY Input: Steps": TSC_XYplot_Steps, |
| "XY Input: CFG Scale": TSC_XYplot_CFG, |
| "XY Input: Sampler": TSC_XYplot_Sampler, |
| "XY Input: Scheduler": TSC_XYplot_Scheduler, |
| "XY Input: Denoise": TSC_XYplot_Denoise, |
| "XY Input: VAE": TSC_XYplot_VAE, |
| "XY Input: Positive Prompt S/R": TSC_XYplot_PromptSR_Positive, |
| "XY Input: Negative Prompt S/R": TSC_XYplot_PromptSR_Negative, |
| "XY Input: Checkpoint": TSC_XYplot_Checkpoint, |
| "XY Input: Clip Skip": TSC_XYplot_ClipSkip, |
| "XY Input: LoRA": TSC_XYplot_LoRA, |
| "XY Input: LoRA Adv.": TSC_XYplot_LoRA_Adv, |
| "XY Input: LoRA Stacks": TSC_XYplot_LoRA_Stacks, |
| "XY Input: Manual XY Entry": TSC_XYplot_Manual_XY_Entry, |
| "Manual XY Entry Info": TSC_XYplot_Manual_XY_Entry_Info, |
| "Join XY Inputs of Same Type": TSC_XYplot_JoinInputs, |
| "Image Overlay": TSC_ImageOverlay, |
| "Evaluate Integers": TSC_EvaluateInts, |
| "Evaluate Floats": TSC_EvaluateFloats, |
| "Evaluate Strings": TSC_EvaluateStrs, |
| "Simple Eval Examples": TSC_EvalExamples |
| } |