| |
| |
| |
| |
|
|
| import numpy as np |
| import math |
| import torch |
| import os |
| from PIL import Image, ImageDraw |
| from ..categories import icons |
| from ..config import color_mapping, COLORS |
| from pywavefront import Wavefront |
| |
| |
|
|
| 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) |
|
|
| def align_text(align_txt, img_center_x, img_center_y, img_width, img_height, pos_x, pos_y, txt_width, txt_height, txt_padding): |
| if align_txt == "center": |
| txt_center_x = img_center_x + pos_x - txt_width / 2 |
| txt_center_y = img_center_y + pos_y - txt_height / 2 |
| elif align_txt == "top left": |
| txt_center_x = pos_x + txt_padding |
| txt_center_y = pos_y + txt_padding |
| if align_txt == "top right": |
| txt_center_x = img_width + pos_x - txt_width - txt_padding |
| txt_center_y = pos_y + txt_padding |
| elif align_txt == "top center": |
| txt_center_x = img_width/2 + pos_x - txt_width/2 - txt_padding |
| txt_center_y = pos_y + txt_padding |
| elif align_txt == "bottom left": |
| txt_center_x = pos_x + txt_padding |
| txt_center_y = img_height + pos_y - txt_height - txt_padding |
| elif align_txt == "bottom right": |
| txt_center_x = img_width + pos_x - txt_width - txt_padding |
| txt_center_y = img_height + pos_y - txt_height - txt_padding |
| elif align_txt == "bottom center": |
| txt_center_x = img_width/2 + pos_x - txt_width/2 - txt_padding |
| txt_center_y = img_height + pos_y - txt_height - txt_padding |
| return (txt_center_x, txt_center_y, ) |
|
|
| |
| class CR_3DPolygon: |
|
|
| @classmethod |
| def INPUT_TYPES(s): |
| |
| shapes = ["cube","tetrahedron"] |
| |
| return {"required": { |
| "shape": (shapes,), |
| "image_width": ("INT", {"default": 512, "min": 64, "max": 2048}), |
| "image_height": ("INT", {"default": 512, "min": 64, "max": 2048}), |
| "radius": ("INT", {"default": 100, "min": 2, "max": 2048}), |
| "distance": ("INT", {"default": 200, "min": 2, "max": 2048}), |
| "rotation_angle": ("FLOAT", {"default": 0, "min": 0, "max": 3600, "step": 0.5}), |
| }, |
| } |
|
|
| RETURN_TYPES = ("IMAGE", ) |
| FUNCTION = "draw_cube" |
| CATEGORY = icons.get("Comfyroll/Graphics/3D") |
| |
| def draw_cube(self, shape, image_width, image_height, radius, distance, rotation_angle=45): |
| |
| size = (image_height, image_width) |
| image = Image.new("RGB", size) |
| draw = ImageDraw.Draw(image) |
|
|
| if shape == "cube": |
| vertices = [ |
| (-radius, -radius, -radius), |
| (radius, -radius, -radius), |
| (radius, radius, -radius), |
| (-radius, radius, -radius), |
| (-radius, -radius, radius), |
| (radius, -radius, radius), |
| (radius, radius, radius), |
| (-radius, radius, radius) |
| ] |
| edges = [ |
| (0, 1), (1, 2), (2, 3), (3, 0), |
| (4, 5), (5, 6), (6, 7), (7, 4), |
| (0, 4), (1, 5), (2, 6), (3, 7) |
| ] |
| elif shape == "tetrahedron": |
| vertices = [ |
| (0, radius, 0), |
| (radius, -radius, -radius), |
| (-radius, -radius, -radius), |
| (0, -radius, radius) |
| ] |
| edges = [ |
| (0, 1), (0, 2), (0, 3), |
| (1, 2), (2, 3), (3, 1) |
| ] |
|
|
| |
| def project_point(point): |
| x, y, z = point |
| x_2d = x * distance / (z + distance) + size[0] / 2 |
| y_2d = y * distance / (z + distance) + size[1] / 2 |
| return x_2d, y_2d |
|
|
| |
| rotated_vertices = [] |
| angle = math.radians(rotation_angle) |
| cos_a = math.cos(angle) |
| sin_a = math.sin(angle) |
| for vertex in vertices: |
| x, y, z = vertex |
| new_x = x * cos_a - z * sin_a |
| new_z = x * sin_a + z * cos_a |
| rotated_vertices.append((new_x, y, new_z)) |
|
|
| |
| for edge in edges: |
| start_point = project_point(rotated_vertices[edge[0]]) |
| end_point = project_point(rotated_vertices[edge[1]]) |
| draw.line([start_point, end_point], fill=(255, 255, 255)) |
|
|
| |
| tensor_image = pil2tensor(image) |
|
|
| return (tensor_image,) |
|
|
| |
| class CR_3DSolids: |
|
|
| @classmethod |
| def INPUT_TYPES(s): |
| return {"required": { |
| "image_width": ("INT", {"default": 512, "min": 64, "max": 2048}), |
| "image_height": ("INT", {"default": 512, "min": 64, "max": 2048}), |
| "radius": ("INT", {"default": 100, "min": 2, "max": 2048}), |
| "height": ("INT", {"default": 100, "min": 2, "max": 2048}), |
| "distance": ("INT", {"default": 200, "min": 2, "max": 2048}), |
| "rotation_angle": ("FLOAT", {"default": 0, "min": 0, "max": 3600, "step": 0.5}), |
| }, |
| } |
|
|
| RETURN_TYPES = ("IMAGE", ) |
| FUNCTION = "draw" |
| CATEGORY = icons.get("Comfyroll/Graphics/3D") |
|
|
| def draw(self, image_width, image_height, radius, height, distance, rotation_angle=45): |
| |
| |
| size = (image_height, image_width) |
| image = Image.new("RGB", size) |
| draw = ImageDraw.Draw(image) |
|
|
| |
| vertices = [ |
| (0, height / 2, 0), |
| (0, -height / 2, 0) |
| ] |
|
|
| num_points = 20 |
| base_points = [ |
| (radius * math.cos(2 * math.pi * i / num_points), -height / 2, radius * math.sin(2 * math.pi * i / num_points)) |
| for i in range(num_points) |
| ] |
| vertices = vertices + base_points |
|
|
| |
| edges = [] |
| for i in range(num_points): |
| edges.append((0, i + 2)) |
| edges.append((1, i + 2)) |
| edges.append((i + 2, (i + 3) if i < num_points - 1 else 2)) |
|
|
| |
| def project_point(point): |
| x, y, z = point |
| x_2d = x * distance / (z + distance) + size[0] / 2 |
| y_2d = y * distance / (z + distance) + size[1] / 2 |
| return x_2d, y_2d |
|
|
| |
| rotated_vertices = [] |
| angle = math.radians(rotation_angle) |
| cos_a = math.cos(angle) |
| sin_a = math.sin(angle) |
| for vertex in vertices: |
| x, y, z = vertex |
| new_x = x * cos_a - z * sin_a |
| new_z = x * sin_a + z * cos_a |
| rotated_vertices.append((new_x, y, new_z)) |
|
|
| |
| colors = [(255, 0, 0), (0, 255, 0), (0, 0, 255)] |
| for i in range(num_points): |
| vertices_indices = [0, i + 2, (i + 3) if i < num_points - 1 else 2] |
| face_vertices = [project_point(rotated_vertices[idx]) for idx in vertices_indices] |
| fill_color = colors[i % 3] |
| draw.polygon(face_vertices, fill=fill_color) |
|
|
| |
| for edge in edges: |
| start_point = project_point(rotated_vertices[edge[0]]) |
| end_point = project_point(rotated_vertices[edge[1]]) |
| draw.line([start_point, end_point], fill=(0, 0, 0)) |
|
|
| |
| tensor_image = pil2tensor(image) |
|
|
| return (tensor_image,) |
|
|
| |
|
|
| class CR_DrawOBJ: |
|
|
| @classmethod |
| def INPUT_TYPES(s): |
| |
| obj_dir = os.path.join(os.path.dirname(os.path.dirname(os.path.realpath(__file__))), "obj") |
| file_list = [f for f in os.listdir(obj_dir) if os.path.isfile(os.path.join(obj_dir, f)) and f.lower().endswith(".obj")] |
|
|
| |
| return {"required": { |
| "image_width": ("INT", {"default": 512, "min": 64, "max": 2048}), |
| "image_height": ("INT", {"default": 512, "min": 64, "max": 2048}), |
| "obj_name": (file_list,), |
| "line_color": (COLORS[1:],), |
| }, |
| } |
|
|
| RETURN_TYPES = ("IMAGE", ) |
| FUNCTION = "draw_wireframe" |
| CATEGORY = icons.get("Comfyroll/Graphics/3D") |
| |
| def draw_wireframe(self, obj_name, image_width=800, image_height=800, line_color="black"): |
|
|
| |
| obj_file = "obj\\" + str(obj_name) |
| resolved_obj_path = os.path.join(os.path.dirname(os.path.dirname(os.path.realpath(__file__))), obj_file) |
| scene = Wavefront(resolved_obj_path) |
|
|
| |
| img = Image.new("RGB", (image_width, image_height), (0, 0, 0)) |
| draw = ImageDraw.Draw(img) |
|
|
| for name, material in scene.materials.items(): |
| for face in material.mesh.faces: |
| vertices = [scene.vertices[i] for i in face] |
|
|
| |
| for i in range(len(vertices)): |
| x1, y1, z1 = vertices[i] |
| x2, y2, z2 = vertices[(i + 1) % len(vertices)] |
|
|
| |
| x1 = int((x1 + 1) * image_width / 2) |
| y1 = int((1 - y1) * image_height / 2) |
| x2 = int((x2 + 1) * image_width / 2) |
| y2 = int((1 - y2) * image_height / 2) |
|
|
| draw.line([(x1, y1), (x2, y2)], fill=line_color) |
|
|
| |
| tensor_image = pil2tensor(img) |
|
|
| return (tensor_image,) |
|
|
| |
| |
| |
| |
| ''' |
| NODE_CLASS_MAPPINGS = { |
| "CR 3D Polygon":CR_3DPolygon, |
| "CR 3D Solids":CR_3DSolids, |
| "CR Draw OBJ":CR_DrawOBJ, |
| } |
| ''' |
|
|
|
|