add flowers
add orig width/height to the grid node debug
This commit is contained in:
+27
-11
@@ -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
@@ -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)
|
||||
|
||||
@@ -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]
|
||||
@@ -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))
|
||||
|
||||
@@ -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))
|
||||
|
||||
|
||||
Reference in New Issue
Block a user