add flowers

add orig width/height to the grid node
debug
This commit is contained in:
frédéric
2024-05-12 22:54:34 +02:00
parent 85fa9a799a
commit 0f93feb3d9
5 changed files with 122 additions and 67 deletions
+27 -11
View File
@@ -55,16 +55,26 @@ class BattlemapGrid:
@classmethod
def INPUT_TYPES(cls):
return {"required": {
"image": ("IMAGE",),
"grid_type": (cls._grid_type, {"default": "square"}),
"grid_side": ("INT", {"default": 64, "min": 10, "max": 2048}),
"line_width": ("INT", {"default": 1, "min": 1, "max": 20}),
"red": ("INT", {"default": 255, "min": 0, "max": 255}),
"green": ("INT", {"default": 255, "min": 0, "max": 255}),
"blue": ("INT", {"default": 255, "min": 0, "max": 255}),
"alpha": ("INT", {"default": 255, "min": 0, "max": 255}),
}
return {
"required": {
"image": ("IMAGE",),
"grid_type": (cls._grid_type, {"default": "square"}),
"grid_side": ("INT", {"default": 64, "min": 10, "max": 2048}),
"line_width": ("INT", {"default": 1, "min": 1, "max": 20}),
"red": ("INT", {"default": 255, "min": 0, "max": 255}),
"green": ("INT", {"default": 255, "min": 0, "max": 255}),
"blue": ("INT", {"default": 255, "min": 0, "max": 255}),
"alpha": ("INT", {"default": 255, "min": 0, "max": 255})
},
"optional": {
"orig_grid_width": ("INT",
{"default": None, "min": 0, "max": 128,
"forceInput": True}),
"orig_grid_height": ("INT",
{"default": None, "min": 00, "max": 128,
"forceInput": True}),
}
}
RETURN_TYPES = ("IMAGE",)
@@ -93,13 +103,19 @@ class BattlemapGrid:
def grid_overlay(self, image: torch.Tensor,
grid_type: str, grid_side: int, line_width: int,
red: int, green: int, blue: int, alpha: int):
red: int, green: int, blue: int, alpha: int,
orig_grid_width=None, orig_grid_height=None):
image_pil = tensor_to_pil(image)
# Create Grid
draw = Draw(image_pil)
pen = Pen((red, green, blue, alpha), line_width)
center = Point(int(image_pil.width / 2), int(image_pil.height / 2))
if (orig_grid_width and orig_grid_height):
width_rate = (image_pil.width / orig_grid_width)
height_rate = (image_pil.height / orig_grid_height)
if 0.99 <= (width_rate / height_rate) <= 1.01:
grid_side = round(image_pil.width / orig_grid_width)
if grid_type == "square":
self.square_grid(image_pil, draw, center, pen,
grid_side)
+72 -46
View File
@@ -1,10 +1,11 @@
from PIL import Image, ImageDraw, ImageColor, ImageFilter
import math
from dataclasses import dataclass
from colorsys import rgb_to_hsv, hsv_to_rgb
from random import random, randint, seed, choice
from .utils import pil_to_tensor, generate_noise
from .point import Point
from colorsys import rgb_to_hsv, hsv_to_rgb
from .path import Path
@dataclass
@@ -14,7 +15,7 @@ class BattlemapMapGenerator:
return {"required": {
"node_seed": (
"INT", {"default": 0, "min": -1, "max": 0xffffffffffffffff,
"step": 1, "label": "seed"}),
"step": 1, "label": "seed", "forceInput": True}),
"grid_width": ("INT", {"default": 24, "min": 10, "max": 128}),
"grid_height": ("INT", {"default": 32, "min": 10, "max": 128}),
"grid_side": ("INT", {"default": 32, "min": 10, "max": 2048}),
@@ -83,12 +84,13 @@ class BattlemapMapGeneratorOutdoors(BattlemapMapGenerator):
"road": (
"BOOLEAN",
{"default": True, "label_off": "OFF", "label_on": "ON"}),
"trees": (
"BOOLEAN",
{"default": True, "label_off": "OFF", "label_on": "ON"}),
"rocks": (
"BOOLEAN",
{"default": True, "label_off": "OFF", "label_on": "ON"}),
"trees": (
"BOOLEAN",
{"default": True, "label_off": "OFF", "label_on": "ON"}),
"positive": ("STRING", {"default": '', "multiline": True}),
"negative": ("STRING", {"default": '', "multiline": True}),
})
@@ -109,7 +111,12 @@ class BattlemapMapGeneratorOutdoors(BattlemapMapGenerator):
return tuple(return_names)
def generator_flowers(self, image, draw):
pass
for i in range(100, 500):
point = Point(randint(0, image.width), randint(0, image.height))
size = randint(2, 6)
draw.ellipse([(point - size).coord(), (point + size).coord()],
fill=choice(list(ImageColor.colormap.keys())),
outline="black", width=2)
def start_point(self, image):
match randint(0, 3):
@@ -124,38 +131,54 @@ class BattlemapMapGeneratorOutdoors(BattlemapMapGenerator):
angle = point.angle_between(Point(image.width / 2, image.height / 2))
return point, angle + randint(-45, 45)
def generate_path(self, image, draw, color,
start_point, start_angle, width=20,
depth=0, max_depth=3):
if depth > max_depth:
def generate_path(self, image, paths: list[Path], start_point, start_angle,
width=20,
depth=0, depth_max=5):
if depth > depth_max or width <= 5:
return
point1, angle, length = start_point, start_angle, randint(100, 150)
point_list = [point1.coord()]
path = Path(width=width)
path.add_point(point1)
while -10 <= point1.x <= image.width + 10 and -10 <= point1.y <= image.height + 10:
point2 = point1.add_polar(length, angle)
point_list.append(point2.coord())
if randint(0, 10) == 0:
self.generate_path(image, draw, color,
point1, angle + randint(-30, 30),
max(5, width + randint(-5, 5)),
depth + 1, max_depth)
path.add_point(point2)
match randint(0, 20):
case 0:
break
case 1 | 2:
new_angle = angle + randint(10, 30) * choice([-1, 1])
self.generate_path(image, paths, point1, new_angle,
width=math.ceil(width * 2 / 3),
depth=depth + 1, depth_max=depth_max)
case _:
pass
point1, angle, length = (point2, angle + randint(-20, 20),
randint(20, 200))
draw.line(point_list, fill="black", width=width + 2, joint="curve")
for i in range(width, 0, -1):
paths.append(path)
def draw_path(self, draw, color, paths: list):
width_max = 0
for path in paths:
width_max = max(width_max, path.width)
draw.line(path.coord(), fill="black", width=path.width + 2,
joint="curve")
for i in range(width_max, 0, -1):
h, s, v = rgb_to_hsv(*ImageColor.getcolor(color, "RGB"))
r, g, b = map(int, hsv_to_rgb(h, s, v / i ** 0.3))
draw.line(point_list, fill=(r, g, b), width=i, joint="curve")
for path in filter(lambda x: x.width <= width_max, paths):
draw.line(path.coord(), fill=(r, g, b), width=i, joint="curve")
def generate_rivers(self, image, draw):
point, angle = self.start_point(image)
color = "blue"
self.generate_path(image, draw, color, point, angle)
paths = list()
self.generate_path(image, paths, point, angle)
self.draw_path(draw, "blue", paths)
def generate_roads(self, image, draw):
point, angle = self.start_point(image)
color = "saddlebrown"
self.generate_path(image, draw, color, point, angle)
paths = list()
self.generate_path(image, paths, point, angle)
self.draw_path(draw, "saddlebrown", paths)
def generate_stars(self, image, draw, size_color):
center = Point(randint(0, image.width), randint(0, image.height))
@@ -164,8 +187,11 @@ class BattlemapMapGeneratorOutdoors(BattlemapMapGenerator):
for angle in range(0, 360, 5):
length = max(size / 5, size * random() * size_multiplicator)
point = center.add_polar(length, angle)
witdh = randint(5, 10)
draw.line([center.coord(), point.coord()],
fill=color, width=7 + randint(-2, 2))
fill="black", width=witdh + 2)
draw.line([center.coord(), point.coord()],
fill=color, width=witdh)
generate_noise(image, center - size, center + size, 10)
def generate_polygon(self, image, draw, size_color, nb_point):
@@ -183,23 +209,23 @@ class BattlemapMapGeneratorOutdoors(BattlemapMapGenerator):
def generate_ellipses(self, image, draw, size_color):
center = Point(randint(0, image.width), randint(0, image.height))
size_multiplicator = random()
max_size = 0
size_max = 0
for size, color in size_color:
max_size = max(max_size, math.ceil(size * size_multiplicator))
size_max = max(size_max, math.ceil(size * size_multiplicator))
point1 = center - math.ceil(size * size_multiplicator) - randint(0,
10)
point2 = center + math.ceil(size * size_multiplicator) + randint(0,
10)
draw.ellipse([point1.coord(), point2.coord()],
fill=color, outline="black", width=2)
generate_noise(image, center - max_size, center + max_size, 10)
generate_noise(image, center - size_max, center + size_max, 10)
def generate_rocks(self, image, draw):
for i in range(10):
self.generate_ellipses(image, draw, [(60, "#111111"),
(50, "darkgray"),
(40, "gray")])
# choice([3, 4, 5, 6, 8, 9, 10])
self.generate_polygon(image, draw, [(50, "#111111"),
(35, "darkgray"),
(25, "gray")],
choice([3, 4, 5, 6, 8, 9]))
def generate_trees(self, image, draw):
for i in range(100):
@@ -209,37 +235,37 @@ class BattlemapMapGeneratorOutdoors(BattlemapMapGenerator):
(20, "lightgreen")])
def map_generator(self, node_seed, grid_width, grid_height,
grid_side, bg_color, river, road, trees, rocks,
grid_side, bg_color, river, road, rocks, trees,
positive, negative):
seed(node_seed)
image_pil, width, height, grid_width, grid_height, grid_side = (
image, width, height, grid_width, grid_height, grid_side = (
self._map_generator(node_seed, grid_width, grid_height, grid_side,
bg_color))
draw = ImageDraw.Draw(image_pil)
draw = ImageDraw.Draw(image)
self.generator_flowers(image, draw)
if river:
self.generate_rivers(image_pil, draw)
self.generate_rivers(image, draw)
positive += "blue river, water."
else:
negative += "blue river, water."
if road:
self.generate_roads(image_pil, draw)
self.generate_roads(image, draw)
positive += "saddlebrown road."
else:
negative += "road."
if trees:
self.generate_trees(image_pil, draw)
positive += "gray rocks."
else:
negative += "rocks."
if rocks:
self.generate_rocks(image_pil, draw)
self.generate_rocks(image, draw)
positive += "green trees."
else:
negative += "trees."
return (pil_to_tensor(image_pil),
if trees:
self.generate_trees(image, draw)
positive += "gray rocks."
else:
negative += "rocks."
return (pil_to_tensor(image),
width, height, grid_width, grid_height, grid_side,
positive, negative)
+14
View File
@@ -0,0 +1,14 @@
from dataclasses import dataclass, field
from .point import Point
@dataclass
class Path:
width: int
points: list[Point] = field(default_factory=list)
def add_point(self, point: Point):
self.points.append(point)
def coord(self):
return [point.coord() for point in self.points]
+6 -7
View File
@@ -1,27 +1,26 @@
from dataclasses import dataclass
import math
from typing import Union
@dataclass
class Point:
x: int
y: int
def __add__(self, value):
def __add__(self, value: Union[int, float, "Point"]) -> "Point":
if isinstance(value, (int, float)):
return Point(self.x + value, self.y + value)
elif isinstance(value, (tuple, list)):
return Point(self.x + value[0], self.y + value[1])
return Point(self.x + value.x, self.y + value.y)
def __sub__(self, value):
def __sub__(self, value: Union[int, float, "Point"]) -> "Point":
if isinstance(value, (int, float)):
return Point(self.x - value, self.y - value)
elif isinstance(value, (tuple, list)):
return Point(self.x - value[0], self.y - value[1])
return Point(self.x - value.x, self.y - value.y)
def __iter__(self):
yield self.x
yield self.y
@@ -29,10 +28,10 @@ class Point:
def coord(self):
return (self.x, self.y)
def add_polar(self, length, angle):
return self + (
def add_polar(self, length: float, angle: float) -> "Point":
return self + Point(
math.ceil(length * math.cos(angle * math.pi / 180)),
-math.ceil(length * math.sin(angle * math.pi / 180)))
def angle_between(self, target):
def angle_between(self, target: "Point") -> float:
return math.degrees(math.atan2(self.y - target.y, target.x - self.x))
+3 -3
View File
@@ -5,20 +5,20 @@ from random import randint
from .point import Point
def tensor_to_pil(image_tensor: torch.tensor):
def tensor_to_pil(image_tensor: torch.tensor) -> Image:
image_np = image_tensor.cpu().numpy()
image_pil = Image.fromarray((image_np.squeeze(0) * 255).astype(np.uint8))
return image_pil.convert("RGBA")
def pil_to_tensor(image_pil: Image):
def pil_to_tensor(image_pil: Image) -> torch.Tensor:
image_tensor_out = torch.tensor(
np.array(image_pil).astype(np.float32) / 255.0)
return torch.unsqueeze(image_tensor_out, 0)
def generate_noise(image, point1: Point, point2: Point, padding=0):
def noise_color(i, j, color_index):
def noise_color(i: int, j: int, color_index: int) -> int:
noise = randint(-64, 64)
return max(0, min(pixels[i, j][color_index] + noise, 255))