Spaces:
Sleeping
Sleeping
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") | |
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)) | |
# Place remaining images | |
for img_file in image_files[1:]: | |
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)) | |
placed = True | |
buffer = BytesIO() | |
canvas.save(buffer, format="PNG") | |
buffer.seek(0) | |
return buffer, canvas | |
# Function to create a base64 download link | |
def create_download_link(image_bytes, filename): | |
b64 = base64.b64encode(image_bytes.getvalue()).decode() | |
href = f'<a href="data:image/png;base64,{b64}" download="{filename}">📥 Download {filename}</a>' | |
return href | |
# Streamlit App | |
st.title("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 | |
# 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 = arrange_images(image_files, canvas_size=(canvas_size, canvas_size)) | |
st.session_state["layout_image"] = layout_image | |
st.session_state["canvas"] = canvas | |
# Display map with scroll and zoom controls | |
if st.session_state["layout_image"] is not None: | |
st.image( | |
st.session_state["layout_image"], | |
caption="Generated Dungeon Map Layout", | |
use_container_width=True, | |
output_format="PNG", | |
clamp=True, | |
) | |
# Sidebar Controls | |
st.sidebar.title("Map Controls") | |
if st.sidebar.button("💾 Save Map"): | |
now = datetime.datetime.now() | |
filename = f"dungeon_map_{now.strftime('%Y%m%d_%H%M%S')}.png" | |
st.session_state["canvas"].save(filename) | |
st.sidebar.success(f"Map saved as {filename}") | |
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 = arrange_images(image_files, canvas_size=(canvas_size, canvas_size)) | |
st.session_state["layout_image"] = layout_image | |
st.session_state["canvas"] = canvas | |
st.rerun() | |
# Zoom controls | |
st.sidebar.title("Zoom") | |
zoom_level = st.sidebar.slider("Zoom Level", min_value=0.5, max_value=2.0, value=1.0, step=0.1) | |
st.image( | |
st.session_state["layout_image"], | |
caption="Zoomed Dungeon Map Layout", | |
use_container_width=False, | |
output_format="PNG", | |
clamp=True, | |
width=int(canvas_size * zoom_level), | |
) | |