awacke1 commited on
Commit
c108613
·
verified ·
1 Parent(s): 284e88f

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +68 -136
app.py CHANGED
@@ -1,156 +1,88 @@
1
  import streamlit as st
2
  import os
3
  from PIL import Image
4
- import random
5
- from io import BytesIO
6
- import datetime
7
  import base64
 
8
 
9
  # Adjust Streamlit layout to wide mode
10
  st.set_page_config(layout="wide")
11
 
12
- # Function to arrange images dynamically
13
- def arrange_images(image_files, canvas_size=(3000, 3000)):
14
- if not image_files:
15
- return None
16
-
17
- positions = [] # Keeps track of image positions (x1, y1, x2, y2)
18
  canvas = Image.new("RGBA", canvas_size, "white")
 
 
19
 
20
- def get_center(pos):
21
- """Calculate center of a bounding box (x1, y1, x2, y2)."""
22
- return ((pos[0] + pos[2]) // 2, (pos[1] + pos[3]) // 2)
23
-
24
- def does_overlap(new_box, existing_boxes):
25
- """Check if a new bounding box overlaps any existing boxes."""
26
- for box in existing_boxes:
27
- if (
28
- new_box[0] < box[2]
29
- and new_box[2] > box[0]
30
- and new_box[1] < box[3]
31
- and new_box[3] > box[1]
32
- ):
33
- return True
34
- return False
35
-
36
- # Place the first image at the center of the canvas
37
- first_img_path = os.path.join(map_dir, image_files[0])
38
- with Image.open(first_img_path) as img:
39
- width, height = img.size
40
- x1 = (canvas_size[0] - width) // 2
41
- y1 = (canvas_size[1] - height) // 2
42
- x2, y2 = x1 + width, y1 + height
43
- positions.append((x1, y1, x2, y2))
44
- canvas.paste(img, (x1, y1))
45
 
46
- # Place remaining images
47
- for img_file in image_files[1:]:
48
- placed = False
49
  img_path = os.path.join(map_dir, img_file)
50
-
51
  with Image.open(img_path) as img:
52
- width, height = img.size
 
53
 
54
- while not placed:
55
- target_box = random.choice(positions)
56
- target_center = get_center(target_box)
57
- side = random.choice(["top", "bottom", "left", "right"])
58
 
59
- if side == "top":
60
- x1 = target_center[0] - width // 2
61
- y1 = target_box[1] - height
62
- elif side == "bottom":
63
- x1 = target_center[0] - width // 2
64
- y1 = target_box[3]
65
- elif side == "left":
66
- x1 = target_box[0] - width
67
- y1 = target_center[1] - height // 2
68
- elif side == "right":
69
- x1 = target_box[2]
70
- y1 = target_center[1] - height // 2
71
 
72
- x2, y2 = x1 + width, y1 + height
73
-
74
- if not does_overlap((x1, y1, x2, y2), positions):
75
- positions.append((x1, y1, x2, y2))
76
- canvas.paste(img, (x1, y1))
77
- placed = True
78
 
 
 
79
  buffer = BytesIO()
80
- canvas.save(buffer, format="PNG")
81
- buffer.seek(0)
82
- return buffer, canvas
83
-
84
- # Function to create a base64 download link
85
- def create_download_link(image_bytes, filename):
86
- b64 = base64.b64encode(image_bytes.getvalue()).decode()
87
- href = f'<a href="data:image/png;base64,{b64}" download="{filename}">📥 Download {filename}</a>'
88
- return href
89
-
90
- # Streamlit App
91
- st.title("Dynamic Dungeon Map Generator")
92
 
93
  # Directory for images
94
- map_dir = "."
95
- canvas_size = 3000
96
-
97
- # Initialize session state
98
- if "layout_image" not in st.session_state:
99
- st.session_state["layout_image"] = None
100
- if "canvas" not in st.session_state:
101
- st.session_state["canvas"] = None
102
-
103
- # Generate map if layout_image is empty
104
- if st.session_state["layout_image"] is None:
105
- image_files = [f for f in os.listdir(map_dir) if f.endswith(".png")]
106
- if image_files:
107
- layout_image, canvas = arrange_images(image_files, canvas_size=(canvas_size, canvas_size))
108
- st.session_state["layout_image"] = layout_image
109
- st.session_state["canvas"] = canvas
110
-
111
- # Display map with scroll and zoom controls
112
- if st.session_state["layout_image"] is not None:
113
- st.image(
114
- st.session_state["layout_image"],
115
- caption="Generated Dungeon Map Layout",
116
- use_container_width=True,
117
- output_format="PNG",
118
- clamp=True,
119
- )
120
-
121
- # Sidebar Controls
122
- st.sidebar.title("Map Controls")
123
- if st.sidebar.button("💾 Save Map"):
124
- now = datetime.datetime.now()
125
- filename = f"dungeon_map_{now.strftime('%Y%m%d_%H%M%S')}.png"
126
- st.session_state["canvas"].save(filename)
127
- st.sidebar.success(f"Map saved as {filename}")
128
-
129
- if st.sidebar.button("🗺️ Regenerate Map"):
130
- image_files = [f for f in os.listdir(map_dir) if f.endswith(".png")]
131
- if image_files:
132
- layout_image, canvas = arrange_images(image_files, canvas_size=(canvas_size, canvas_size))
133
- st.session_state["layout_image"] = layout_image
134
- st.session_state["canvas"] = canvas
135
- st.rerun()
136
-
137
- # Zoom controls
138
- st.sidebar.title("Zoom")
139
- zoom_level = st.sidebar.slider("Zoom Level", min_value=0.5, max_value=2.0, value=1.0, step=0.1)
140
-
141
- # Resize the canvas for zoom functionality
142
- if st.session_state["canvas"] is not None:
143
- zoomed_canvas = st.session_state["canvas"].resize(
144
- (int(canvas_size * zoom_level), int(canvas_size * zoom_level)),
145
- resample=Image.ANTIALIAS
146
- )
147
- buffer = BytesIO()
148
- zoomed_canvas.save(buffer, format="PNG")
149
- buffer.seek(0)
150
-
151
- st.image(
152
- buffer,
153
- caption=f"Zoomed Dungeon Map Layout (Zoom: {zoom_level}x)",
154
- use_container_width=False,
155
- output_format="PNG",
156
- )
 
1
  import streamlit as st
2
  import os
3
  from PIL import Image
 
 
 
4
  import base64
5
+ from io import BytesIO
6
 
7
  # Adjust Streamlit layout to wide mode
8
  st.set_page_config(layout="wide")
9
 
10
+ # Generate Map as an Image
11
+ def generate_map(image_files, canvas_size=(3000, 3000)):
 
 
 
 
12
  canvas = Image.new("RGBA", canvas_size, "white")
13
+ if not image_files:
14
+ return canvas
15
 
16
+ grid_size = int(len(image_files) ** 0.5) + 1 # Calculate grid size
17
+ img_size = canvas_size[0] // grid_size # Size for each image cell
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
18
 
19
+ for idx, img_file in enumerate(image_files):
 
 
20
  img_path = os.path.join(map_dir, img_file)
 
21
  with Image.open(img_path) as img:
22
+ # Maintain aspect ratio
23
+ img.thumbnail((img_size, img_size), Image.Resampling.LANCZOS)
24
 
25
+ # Calculate grid position
26
+ row, col = divmod(idx, grid_size)
27
+ x = col * img_size
28
+ y = row * img_size
29
 
30
+ # Paste the image on the canvas
31
+ canvas.paste(img, (x, y), mask=img if img.mode == "RGBA" else None)
 
 
 
 
 
 
 
 
 
 
32
 
33
+ return canvas
 
 
 
 
 
34
 
35
+ # Encode Image as Base64 for Leaflet
36
+ def encode_image_to_base64(image):
37
  buffer = BytesIO()
38
+ image.save(buffer, format="PNG")
39
+ encoded = base64.b64encode(buffer.getvalue()).decode()
40
+ return encoded
 
 
 
 
 
 
 
 
 
41
 
42
  # Directory for images
43
+ map_dir = "." # Replace with your directory containing images
44
+ image_files = [f for f in os.listdir(map_dir) if f.endswith(".png")]
45
+
46
+ # Generate the map
47
+ canvas_size = (3000, 3000)
48
+ canvas = generate_map(image_files, canvas_size)
49
+ encoded_image = encode_image_to_base64(canvas)
50
+
51
+ # Leaflet HTML Template
52
+ leaflet_html = f"""
53
+ <!DOCTYPE html>
54
+ <html>
55
+ <head>
56
+ <link rel="stylesheet" href="https://unpkg.com/[email protected]/dist/leaflet.css" />
57
+ <script src="https://unpkg.com/[email protected]/dist/leaflet.js"></script>
58
+ <style>
59
+ #map {{
60
+ height: 100vh;
61
+ }}
62
+ </style>
63
+ </head>
64
+ <body>
65
+ <div id="map"></div>
66
+ <script>
67
+ // Initialize the map
68
+ var map = L.map('map').setView([0, 0], 1);
69
+
70
+ // Add the image overlay
71
+ var bounds = [[-1500, -1500], [1500, 1500]];
72
+ var image = L.imageOverlay("data:image/png;base64,{encoded_image}", bounds).addTo(map);
73
+
74
+ // Set the map view to the bounds of the image
75
+ map.fitBounds(bounds);
76
+
77
+ // Add zoom controls
78
+ L.control.zoom({{
79
+ position: 'topright'
80
+ }}).addTo(map);
81
+ </script>
82
+ </body>
83
+ </html>
84
+ """
85
+
86
+ # Embed Leaflet Map in Streamlit
87
+ st.title("Interactive Dungeon Map")
88
+ st.components.v1.html(leaflet_html, height=600)