multimodalart's picture
Squashing commit
4450790 verified
#---------------------------------------------------------------------------------------------------------------------#
# Comfyroll Studio custom nodes by RockOfFire and Akatsuzi https://github.com/Suzie1/ComfyUI_Comfyroll_CustomNodes
# for ComfyUI https://github.com/comfyanonymous/ComfyUI
#---------------------------------------------------------------------------------------------------------------------#
import numpy as np
import torch
import random
import os
import math
from PIL import Image, ImageDraw
from .functions_graphics import get_color_values
from .shapes import (
draw_circle, draw_oval, draw_diamond, draw_square,
draw_triangle, draw_hexagon, draw_octagon,
draw_half_circle, draw_quarter_circle, draw_starburst, draw_star, draw_cross
)
from ..categories import icons
from ..config import color_mapping, COLORS
def tensor2pil(image):
return Image.fromarray(np.clip(255. * image.cpu().numpy().squeeze(), 0, 255).astype(np.uint8))
def pil2tensor(image):
return torch.from_numpy(np.array(image).astype(np.float32) / 255.0).unsqueeze(0)
#---------------------------------------------------------------------------------------------------------------------#
class CR_BinaryPatternSimple:
@classmethod
def INPUT_TYPES(cls):
return {"required": {
"binary_pattern": ("STRING", {"multiline": True, "default": "10101"}),
"width": ("INT", {"default": 512, "min": 64, "max": 4096}),
"height": ("INT", {"default": 512, "min": 64, "max": 4096}),
}
}
RETURN_TYPES = ("IMAGE", "STRING", )
RETURN_NAMES = ("IMAGE", "show_help", )
FUNCTION = "draw_pattern"
CATEGORY = icons.get("Comfyroll/Graphics/Pattern")
def draw_pattern(self, binary_pattern, width, height):
# Convert multiline binary pattern to a 2D list
rows = binary_pattern.strip().split('\n')
grid = [[int(bit) for bit in row.strip()] for row in rows]
# Calculate the size of each square
square_width = width // len(rows[0])
square_height = height // len(rows)
# Create a new image
image = Image.new("RGB", (width, height), color='black')
draw = ImageDraw.Draw(image)
# Draw grid based on the binary pattern
for row_index, row in enumerate(grid):
for col_index, bit in enumerate(row):
x1 = col_index * square_width
y1 = row_index * square_height
x2 = x1 + square_width
y2 = y1 + square_height
# Draw black square if bit is 1, else draw white square
color = 'black' if bit == 1 else 'white'
draw.rectangle([x1, y1, x2, y2], fill=color, outline="black")
image_out = pil2tensor(image)
show_help = "https://github.com/Suzie1/ComfyUI_Comfyroll_CustomNodes/wiki/Pattern-Nodes-2#cr-simple-binary-pattern"
# Convert the PIL image back to a torch tensor
return (image_out, show_help, )
#---------------------------------------------------------------------------------------------------------------------#
class CR_BinaryPattern:
@classmethod
def INPUT_TYPES(cls):
return {"required": {
"binary_pattern": ("STRING", {"multiline": True, "default": "10101"}),
"width": ("INT", {"default": 512, "min": 64, "max": 4096}),
"height": ("INT", {"default": 512, "min": 64, "max": 4096}),
"background_color": (COLORS,),
"color_0": (COLORS,),
"color_1": (COLORS,),
"outline_thickness": ("INT", {"default": 0, "min": 0, "max": 1024}),
"outline_color": (COLORS,),
"jitter_distance": ("INT", {"default": 0, "min": 0, "max": 1024}),
"bias": ("FLOAT", {"default": 0.50, "min": 0.00, "max": 1.00, "step": 0.05}),
},
"optional": {
"bg_color_hex": ("STRING", {"multiline": False, "default": "#000000"}),
"color0_hex": ("STRING", {"multiline": False, "default": "#000000"}),
"color1_hex": ("STRING", {"multiline": False, "default": "#000000"}),
"outline_color_hex": ("STRING", {"multiline": False, "default": "#000000"}),
}
}
RETURN_TYPES = ("IMAGE", "STRING", )
RETURN_NAMES = ("IMAGE", "show_help", )
FUNCTION = "draw_pattern"
CATEGORY = icons.get("Comfyroll/Graphics/Pattern")
def draw_pattern(self, binary_pattern, width, height,
background_color, outline_color,
color_0="white", color_1="black", outline_thickness=0,
color0_hex='#000000', color1_hex='#000000',
bg_color_hex='#000000', outline_color_hex='#000000',
jitter_distance = 0, bias=0.5):
# Get RGB values
color0 = get_color_values(color_0, color0_hex, color_mapping)
color1 = get_color_values(color_1, color1_hex, color_mapping)
bg_color = get_color_values(background_color, bg_color_hex, color_mapping)
outline_color = get_color_values(outline_color, outline_color_hex, color_mapping)
# Convert multiline binary pattern to a 2D list
rows = binary_pattern.strip().split('\n')
grid = [[int(bit) for bit in row.strip()] for row in rows]
# Calculate the size of each square
square_width = width / len(rows[0])
square_height = height / len(rows)
# Create a new image
image = Image.new("RGB", (width, height), color=bg_color)
draw = ImageDraw.Draw(image)
x_jitter = 0
y_jitter = 0
# Draw grid based on the binary pattern
for row_index, row in enumerate(grid):
for col_index, bit in enumerate(row):
if jitter_distance != 0:
x_jitter = random.uniform(0, jitter_distance)
y_jitter = random.uniform(0, jitter_distance)
x1 = col_index * square_width + x_jitter
y1 = row_index * square_height + y_jitter
x2 = x1 + square_width + x_jitter
y2 = y1 + square_height + y_jitter
# Draw black square if bit is 1, else draw white square
#color = color1 if bit == 1 else color0
# Adjust color based on bias
if random.uniform(0, 1) < abs(bias):
color = color1
else:
color = color0
draw.rectangle([x1, y1, x2, y2], fill=color, outline=outline_color, width=outline_thickness)
image_out = pil2tensor(image)
show_help = "https://github.com/Suzie1/ComfyUI_Comfyroll_CustomNodes/wiki/Pattern-Nodes-2#cr-binary-pattern"
# Convert the PIL image back to a torch tensor
return (image_out, show_help, )
#---------------------------------------------------------------------------------------------------------------------#class CR_DrawShape:
class CR_DrawShape:
@classmethod
def INPUT_TYPES(cls):
shapes = ["circle","oval","square","diamond","triangle","hexagon","octagon",
"quarter circle","half circle","quarter circle",
"starburst","star","cross",
"diagonal regions"]
return {"required": {
"width": ("INT", {"default": 512, "min": 64, "max": 4096}),
"height": ("INT", {"default": 512, "min": 64, "max": 4096}),
"shape": (shapes,),
"shape_color": (COLORS,),
"back_color": (COLORS,),
"x_offset": ("INT", {"default": 0, "min": -2048, "max": 2048}),
"y_offset": ("INT", {"default": 0, "min": -2048, "max": 2048}),
"zoom": ("FLOAT", {"default": 1.00, "min": 0.00, "max": 10.00, "step": 0.05}),
"rotation": ("FLOAT", {"default": 0.0, "min": 0.0, "max": 3600.0, "step": 0.1}),
},
"optional": {
"shape_color_hex": ("STRING", {"multiline": False, "default": "#000000"}),
"bg_color_hex": ("STRING", {"multiline": False, "default": "#000000"}),
}
}
RETURN_TYPES = ("IMAGE", "STRING", )
RETURN_NAMES = ("IMAGE", "show_help", )
FUNCTION = "make_shape"
CATEGORY = icons.get("Comfyroll/Graphics/Shape")
def make_shape(self, width, height, rotation,
shape, shape_color, back_color,
x_offset=0, y_offset=0, zoom=1.0,
shape_color_hex='#000000', bg_color_hex='#000000'):
bg_color = get_color_values(back_color, bg_color_hex, color_mapping)
shape_color = get_color_values(shape_color, shape_color_hex, color_mapping)
back_img = Image.new("RGB", (width, height), color=bg_color)
shape_img = Image.new("RGB", (width, height), color=shape_color)
shape_mask = Image.new('L', (width, height))
draw = ImageDraw.Draw(shape_mask)
center_x = width // 2 + x_offset
center_y = height // 2 + y_offset
size = min(width - x_offset, height - y_offset) * zoom
aspect_ratio = width / height
color = 'white'
shape_functions = {
'circle': draw_circle,
'oval': draw_oval,
'diamond': draw_diamond,
'square': draw_square,
'triangle': draw_triangle,
'hexagon': draw_hexagon,
'octagon': draw_octagon,
'quarter circle': draw_quarter_circle,
'half circle': draw_half_circle,
'starburst': draw_starburst,
'star': draw_star,
'cross': draw_cross,
}
if shape in shape_functions:
shape_function = shape_functions.get(shape)
shape_function(draw, center_x, center_y, size, aspect_ratio, color)
if shape == "diagonal regions":
draw.polygon([(width, 0), (width, height), (0, height)], fill=color)
shape_mask = shape_mask.rotate(rotation, center=(center_x, center_y))
result_image = Image.composite(shape_img, back_img, shape_mask)
image_out = pil2tensor(result_image)
show_help = "https://github.com/Suzie1/ComfyUI_Comfyroll_CustomNodes/wiki/Pattern-Nodes-2#cr-draw-shape"
return (image_out, show_help, )
#---------------------------------------------------------------------------------------------------------------------#class CR_DrawShape:
'''
class CR_ShapeScheduler:
@classmethod
def INPUT_TYPES(cls):
shapes = ["circle","oval","square","diamond","triangle","hexagon","octagon",
"quarter circle","half circle","quarter circle","starburst","star","cross","diagonal regions"]
return {"required": {
"width": ("INT", {"default": 512, "min": 64, "max": 4096}),
"height": ("INT", {"default": 512, "min": 64, "max": 4096}),
"shape": (shapes,),
"shape_color": (COLORS,),
"back_color": (COLORS,),
"x_offset": ("INT", {"default": 0, "min": -2048, "max": 2048}),
"y_offset": ("INT", {"default": 0, "min": -2048, "max": 2048}),
"zoom": ("FLOAT", {"default": 1.00, "min": 0.00, "max": 10.00, "step": 0.05}),
"rotation": ("FLOAT", {"default": 0.0, "min": 0.0, "max": 3600.0, "step": 0.1}),
"schedule": ("STRING", {"multiline": True, "default": "schedule"}),
},
"optional": {
"shape_color_hex": ("STRING", {"multiline": False, "default": "#000000"}),
"bg_color_hex": ("STRING", {"multiline": False, "default": "#000000"}),
}
}
RETURN_TYPES = ("IMAGE", "STRING", )
RETURN_NAMES = ("IMAGE", "show_help", )
FUNCTION = "make_shape"
CATEGORY = icons.get("Comfyroll/Graphics/Shape")
def make_shape(self, width, height, rotation,
shape, shape_color, back_color, schedule,
x_offset=0, y_offset=0, zoom=1.0,
shape_color_hex='#000000', bg_color_hex='#000000'):
bg_color = get_color_values(back_color, bg_color_hex, color_mapping)
shape_color = get_color_values(shape_color, shape_color_hex, color_mapping)
back_img = Image.new("RGB", (width, height), color=bg_color)
shape_img = Image.new("RGB", (width, height), color=shape_color)
shape_mask = Image.new('L', (width, height))
draw = ImageDraw.Draw(shape_mask)
center_x = width // 2 + x_offset
center_y = height // 2 + y_offset
size = min(width - x_offset, height - y_offset) * zoom
aspect_ratio = width / height
num_rays = 16
color = 'white'
shape_functions = {
'circle': draw_circle,
'oval': draw_oval,
'diamond': draw_diamond,
'square': draw_square,
'triangle': draw_triangle,
'hexagon': draw_hexagon,
'octagon': draw_octagon,
'quarter circle': draw_quarter_circle,
'half circle': draw_half_circle,
'starburst': draw_starburst,
'star': draw_star,
'cross': draw_cross,
}
if shape in shape_functions:
shape_function = shape_functions.get(shape)
shape_function(draw, center_x, center_y, size, aspect_ratio, color)
if shape == "diagonal regions":
draw.polygon([(width, 0), (width, height), (0, height)], fill=color)
shape_mask = shape_mask.rotate(rotation, center=(center_x, center_y))
result_image = Image.composite(shape_img, back_img, shape_mask)
image_out = pil2tensor(result_image)
show_help = "https://github.com/Suzie1/ComfyUI_Comfyroll_CustomNodes/wiki/Pattern-Nodes-2#cr-draw-shape"
return (image_out, show_help, )
'''
#---------------------------------------------------------------------------------------------------------------------#class CR_DrawShape:
class CR_DrawPie:
@classmethod
def INPUT_TYPES(cls):
return {"required": {
"width": ("INT", {"default": 512, "min": 64, "max": 4096}),
"height": ("INT", {"default": 512, "min": 64, "max": 4096}),
"pie_start": ("FLOAT", {"default": 30.0, "min": 0.0, "max": 9999.0, "step": 0.1}),
"pie_stop": ("FLOAT", {"default": 330.0, "min": 0.0, "max": 9999.0, "step": 0.1}),
"shape_color": (COLORS,),
"back_color": (COLORS,),
"x_offset": ("INT", {"default": 0, "min": -2048, "max": 2048}),
"y_offset": ("INT", {"default": 0, "min": -2048, "max": 2048}),
"zoom": ("FLOAT", {"default": 1.00, "min": 0.00, "max": 10.00, "step": 0.05}),
"rotation": ("FLOAT", {"default": 0.0, "min": 0.0, "max": 3600.0, "step": 0.1}),
},
"optional": {
"shape_color_hex": ("STRING", {"multiline": False, "default": "#000000"}),
"bg_color_hex": ("STRING", {"multiline": False, "default": "#000000"}),
}
}
RETURN_TYPES = ("IMAGE", "STRING", )
RETURN_NAMES = ("IMAGE", "show_help", )
FUNCTION = "make_shape"
CATEGORY = icons.get("Comfyroll/Graphics/Shape")
def make_shape(self, width, height, rotation,
pie_start, pie_stop, shape_color, back_color,
x_offset=0, y_offset=0, zoom=1.0,
shape_color_hex='#000000', bg_color_hex='#000000'):
bg_color = get_color_values(back_color, bg_color_hex, color_mapping)
shape_color = get_color_values(shape_color, shape_color_hex, color_mapping)
back_img = Image.new("RGB", (width, height), color=bg_color)
shape_img = Image.new("RGBA", (width, height), color=(0, 0, 0, 0))
draw = ImageDraw.Draw(shape_img, 'RGBA')
center_x = width // 2 + x_offset
center_y = height // 2 + y_offset
size = min(width - x_offset, height - y_offset) * zoom
aspect_ratio = width / height
num_rays = 16
color = 'white'
draw.pieslice([(center_x - size / 2, center_y - size / 2),
(center_x + size / 2, center_y + size / 2)], start=pie_start, end=pie_stop, fill=color, outline=None)
shape_img = shape_img.rotate(rotation, center=(center_x, center_y))
# Composite the images with anti-aliasing
result_image = Image.alpha_composite(back_img.convert("RGBA"), shape_img)
image_out = pil2tensor(result_image.convert("RGB"))
show_help = "https://github.com/Suzie1/ComfyUI_Comfyroll_CustomNodes/wiki/Pattern-Nodes-2#cr-draw-pie"
return (image_out, show_help, )
#---------------------------------------------------------------------------------------------------------------------#
class CR_RandomShapePattern:
@classmethod
def INPUT_TYPES(cls):
shapes = ["circle","oval","square","diamond","triangle",
"hexagon","octagon","half circle","quarter circle",
"starburst","star", "cross"]
return {"required": {
"width": ("INT", {"default": 512, "min": 64, "max": 4096}),
"height": ("INT", {"default": 512, "min": 64, "max": 4096}),
"num_rows": ("INT", {"default": 5, "min": 1, "max": 128}),
"num_cols": ("INT", {"default": 5, "min": 1, "max": 128}),
"color1": (COLORS,),
"color2": (COLORS,),
},
"optional": {
"color1_hex": ("STRING", {"multiline": False, "default": "#000000"}),
"color2_hex": ("STRING", {"multiline": False, "default": "#000000"}),
}
}
RETURN_TYPES = ("IMAGE", "STRING", )
RETURN_NAMES = ("IMAGE", "show_help", )
FUNCTION = "plot_random_shapes"
CATEGORY = icons.get("Comfyroll/Graphics/Shape")
def plot_random_shapes(self, num_rows, num_cols, width, height, color1, color2,
color1_hex="#000000", color2_hex="#000000"):
color1 = get_color_values(color1, color1_hex, color_mapping)
color2 = get_color_values(color2, color2_hex, color_mapping)
image = Image.new("RGB", (width, height), color="white")
draw = ImageDraw.Draw(image)
shape_functions = [
draw_circle,
draw_oval,
draw_diamond,
draw_square,
draw_triangle,
draw_hexagon,
draw_octagon,
draw_half_circle,
draw_quarter_circle,
draw_starburst,
draw_star,
draw_cross,
]
for row in range(num_rows):
for col in range(num_cols):
shape_function = random.choice(shape_functions)
color = random.choice([color1, color2])
size = random.uniform(20, min(width, height) / 2)
aspect_ratio = random.uniform(0.5, 2.0) # For shapes that use aspect ratio
center_x = col * (width / num_cols) + (width / num_cols) / 2
center_y = row * (height / num_rows) + (height / num_rows) / 2
shape_function(draw, center_x, center_y, size, aspect_ratio, color)
image_out = pil2tensor(image)
show_help = "https://github.com/Suzie1/ComfyUI_Comfyroll_CustomNodes/wiki/Pattern-Nodes-2#cr-random-shape-pattern"
return (image_out, show_help, )
#---------------------------------------------------------------------------------------------------------------------#
# MAPPINGS
#---------------------------------------------------------------------------------------------------------------------#
# For reference only, actual mappings are in __init__.py
'''
NODE_CLASS_MAPPINGS = {
"CR Simple Binary Pattern Simple": CR Binary Pattern Simple,
"CR Binary Pattern": CR_BinaryPattern,
"CR Draw Shape": CR_DrawShape,
"CR Random Shape Pattern: CR_RandomShapePattern,
"CR Draw Pie": CR_DrawPie,
}
'''