Spaces:
Running
Running
File size: 6,729 Bytes
c072d5d |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 |
import streamlit as st
import os
from PIL import Image
import random
from io import BytesIO
import datetime
import base64
# Adjust Streamlit layout to wide mode
st.set_page_config(layout="wide")
# Function to arrange images dynamically
def arrange_images(image_files, canvas_size=(3000, 3000)):
if not image_files:
return None, []
positions = [] # Keeps track of image positions (x1, y1, x2, y2)
canvas = Image.new("RGBA", canvas_size, "white")
room_details = [] # To store room layout details
def get_center(pos):
"""Calculate center of a bounding box (x1, y1, x2, y2)."""
return ((pos[0] + pos[2]) // 2, (pos[1] + pos[3]) // 2)
def does_overlap(new_box, existing_boxes):
"""Check if a new bounding box overlaps any existing boxes."""
for box in existing_boxes:
if (
new_box[0] < box[2]
and new_box[2] > box[0]
and new_box[1] < box[3]
and new_box[3] > box[1]
):
return True
return False
# Place the first image at the center of the canvas
first_img_path = os.path.join(map_dir, image_files[0])
with Image.open(first_img_path) as img:
width, height = img.size
x1 = (canvas_size[0] - width) // 2
y1 = (canvas_size[1] - height) // 2
x2, y2 = x1 + width, y1 + height
positions.append((x1, y1, x2, y2))
canvas.paste(img, (x1, y1))
room_details.append(f"Room 1: {image_files[0]} at center")
# Place remaining images
for idx, img_file in enumerate(image_files[1:], start=2):
placed = False
img_path = os.path.join(map_dir, img_file)
with Image.open(img_path) as img:
width, height = img.size
while not placed:
target_box = random.choice(positions)
target_center = get_center(target_box)
side = random.choice(["top", "bottom", "left", "right"])
if side == "top":
x1 = target_center[0] - width // 2
y1 = target_box[1] - height
elif side == "bottom":
x1 = target_center[0] - width // 2
y1 = target_box[3]
elif side == "left":
x1 = target_box[0] - width
y1 = target_center[1] - height // 2
elif side == "right":
x1 = target_box[2]
y1 = target_center[1] - height // 2
x2, y2 = x1 + width, y1 + height
if not does_overlap((x1, y1, x2, y2), positions):
positions.append((x1, y1, x2, y2))
canvas.paste(img, (x1, y1))
room_details.append(f"Room {idx}: {img_file} at ({x1}, {y1})")
placed = True
buffer = BytesIO()
canvas.save(buffer, format="PNG")
buffer.seek(0)
return buffer, canvas, room_details
# Function to create a base64 link
def create_base64_download_link(file_content, filename, file_type):
b64 = base64.b64encode(file_content.encode() if file_type == "txt" else file_content).decode()
return f"[π₯ Download {filename}](data:application/{file_type};base64,{b64})"
# Sidebar Title
st.sidebar.markdown("#### π° Dynamic Dungeon Map Generator")
# Directory for images
map_dir = "."
canvas_size = 3000
# Initialize session state
if "layout_image" not in st.session_state:
st.session_state["layout_image"] = None
if "canvas" not in st.session_state:
st.session_state["canvas"] = None
if "room_details" not in st.session_state:
st.session_state["room_details"] = []
if "saved_files" not in st.session_state:
st.session_state["saved_files"] = []
# Generate map if layout_image is empty
if st.session_state["layout_image"] is None:
image_files = [f for f in os.listdir(map_dir) if f.endswith(".png")]
if image_files:
layout_image, canvas, room_details = arrange_images(image_files, canvas_size=(canvas_size, canvas_size))
st.session_state["layout_image"] = layout_image
st.session_state["canvas"] = canvas
st.session_state["room_details"] = room_details
# Sidebar Controls
if st.sidebar.button("πΎ Save Map"):
now = datetime.datetime.now()
filename_png = f"dungeon_map_{now.strftime('%Y%m%d_%H%M%S')}.png"
filename_txt = f"room_layout_{now.strftime('%Y%m%d_%H%M%S')}.txt"
# Save the PNG map
st.session_state["canvas"].save(filename_png)
st.sidebar.success(f"Map saved as {filename_png}")
# Save room details as a text file
room_details_content = "\n".join(st.session_state["room_details"])
with open(filename_txt, "w") as f:
f.write(room_details_content)
st.sidebar.success(f"Room layout saved as {filename_txt}")
# Generate base64 download links
with open(filename_png, "rb") as f:
png_base64 = create_base64_download_link(f.read(), filename_png, "octet-stream")
txt_base64 = create_base64_download_link(room_details_content, filename_txt, "txt")
# Add files to save history
st.session_state["saved_files"].append((png_base64, txt_base64))
if st.sidebar.button("πΊοΈ Regenerate Map"):
image_files = [f for f in os.listdir(map_dir) if f.endswith(".png")]
if image_files:
layout_image, canvas, room_details = arrange_images(image_files, canvas_size=(canvas_size, canvas_size))
st.session_state["layout_image"] = layout_image
st.session_state["canvas"] = canvas
st.session_state["room_details"] = room_details
st.rerun()
# Display save history in the sidebar
st.sidebar.markdown("### π Save History")
for idx, (png_link, txt_link) in enumerate(st.session_state["saved_files"], start=1):
st.sidebar.markdown(f"{idx}. {png_link} | {txt_link}")
# Zoom Controls
st.sidebar.title("Zoom")
show_zoomed = st.sidebar.checkbox("Show Zoomed Version")
zoom_level = st.sidebar.slider("Zoom Level", min_value=0.1, max_value=2.0, value=1.0, step=0.1)
if show_zoomed and st.session_state["canvas"] is not None:
zoomed_canvas = st.session_state["canvas"].resize(
(int(canvas_size * zoom_level), int(canvas_size * zoom_level)),
resample=Image.Resampling.LANCZOS,
)
buffer = BytesIO()
zoomed_canvas.save(buffer, format="PNG")
buffer.seek(0)
st.image(
buffer,
caption=f"Zoomed Dungeon Map Layout (Zoom Level: {zoom_level}x)",
use_container_width=False,
output_format="PNG",
)
else:
st.image(
st.session_state["layout_image"],
caption="Generated Dungeon Map Layout",
use_container_width=True,
output_format="PNG",
)
|