Update app.py
Browse files
app.py
CHANGED
@@ -1,9 +1,9 @@
|
|
1 |
import streamlit as st
|
2 |
import streamlit.components.v1 as components
|
3 |
|
4 |
-
st.set_page_config(page_title="City Evolution Simulator", layout="wide")
|
5 |
|
6 |
-
st.title("City Evolution Simulator")
|
7 |
st.write("Watch a 3D city grow with lakes, hills, and evolving blocks")
|
8 |
|
9 |
html_code = """
|
@@ -11,9 +11,10 @@ html_code = """
|
|
11 |
<html>
|
12 |
<head>
|
13 |
<meta charset="utf-8">
|
14 |
-
<title>City Evolution Simulator</title>
|
15 |
<style>
|
16 |
body { margin: 0; overflow: hidden; }
|
|
|
17 |
canvas { width: 100%; height: 100%; display: block; }
|
18 |
.ui-panel {
|
19 |
position: absolute;
|
@@ -40,6 +41,7 @@ html_code = """
|
|
40 |
</style>
|
41 |
</head>
|
42 |
<body>
|
|
|
43 |
<div class="ui-panel">
|
44 |
<h3>City Controls</h3>
|
45 |
<button id="evolve">Evolve City</button>
|
@@ -89,8 +91,8 @@ html_code = """
|
|
89 |
const stack = [];
|
90 |
let position = basePos.clone();
|
91 |
let direction = new THREE.Vector3(0, 1, 0);
|
92 |
-
|
93 |
const structure = new THREE.Group();
|
|
|
94 |
for (let char of this.generate()) {
|
95 |
switch(char) {
|
96 |
case 'F':
|
@@ -131,7 +133,7 @@ html_code = """
|
|
131 |
class CitySimulator {
|
132 |
constructor() {
|
133 |
this.blocks = [];
|
134 |
-
this.blockSize = 10;
|
135 |
this.maxBuildingsPerBlock = 5;
|
136 |
this.generation = 0;
|
137 |
this.lakeCenters = [
|
@@ -169,14 +171,14 @@ html_code = """
|
|
169 |
|
170 |
const building = lsystem.build(scene, basePos, block.maxHeight);
|
171 |
if (this.isWaterfront(block.position.x, block.position.y)) {
|
172 |
-
building.scale.set(1.5, 2, 1.5);
|
173 |
}
|
174 |
scene.add(building);
|
175 |
block.buildings.push(building);
|
176 |
}
|
177 |
}
|
178 |
|
179 |
-
evolve() {
|
180 |
this.generation++;
|
181 |
if (this.blocks.length < 20) {
|
182 |
const x = (Math.random() - 0.5) * 140;
|
@@ -208,25 +210,33 @@ html_code = """
|
|
208 |
let city;
|
209 |
|
210 |
function init() {
|
211 |
-
const container = document.
|
|
|
|
|
|
|
|
|
|
|
|
|
212 |
scene = new THREE.Scene();
|
213 |
scene.background = new THREE.Color(0x87CEEB);
|
214 |
|
215 |
-
// Camera
|
216 |
camera = new THREE.PerspectiveCamera(75, 16 / 9, 0.1, 1000);
|
217 |
camera.position.set(0, 50, 80);
|
218 |
|
|
|
219 |
renderer = new THREE.WebGLRenderer({ antialias: true });
|
220 |
renderer.setSize(window.innerWidth, window.innerHeight * (9/16));
|
221 |
container.appendChild(renderer.domElement);
|
222 |
|
223 |
// Lights
|
224 |
-
|
|
|
225 |
const sun = new THREE.DirectionalLight(0xffffff, 0.8);
|
226 |
sun.position.set(50, 50, 50);
|
227 |
scene.add(sun);
|
228 |
|
229 |
-
//
|
230 |
const groundGeo = new THREE.PlaneGeometry(160, 90, 32, 32);
|
231 |
const groundMat = new THREE.MeshPhongMaterial({ color: 0x4a7043 });
|
232 |
const ground = new THREE.Mesh(groundGeo, groundMat);
|
@@ -234,35 +244,36 @@ html_code = """
|
|
234 |
ground.position.y = -0.1;
|
235 |
scene.add(ground);
|
236 |
|
237 |
-
//
|
238 |
const lakeGeo = new THREE.CircleGeometry(10, 32);
|
239 |
const lakeMat = new THREE.MeshPhongMaterial({ color: 0x4682b4 });
|
240 |
-
|
|
|
241 |
const lake = new THREE.Mesh(lakeGeo, lakeMat);
|
242 |
lake.rotation.x = -Math.PI / 2;
|
243 |
-
lake.position.set(center.x, 0, center.y);
|
244 |
scene.add(lake);
|
245 |
});
|
246 |
|
247 |
-
//
|
248 |
const bridgeGeo = new THREE.BoxGeometry(5, 0.2, 15);
|
249 |
const bridgeMat = new THREE.MeshPhongMaterial({ color: 0x808080 });
|
250 |
-
const
|
251 |
-
|
252 |
-
scene.add(
|
253 |
|
254 |
// Controls
|
255 |
controls = new THREE.OrbitControls(camera, renderer.domElement);
|
256 |
controls.enableDamping = true;
|
257 |
controls.target.set(0, 0, 0);
|
258 |
|
259 |
-
//
|
260 |
city = new CitySimulator();
|
261 |
city.addBlock(scene, 0, 0);
|
262 |
|
263 |
// Events
|
264 |
window.addEventListener('resize', onWindowResize);
|
265 |
-
document.getElementById('evolve').addEventListener('click', () => city.evolve());
|
266 |
document.getElementById('reset').addEventListener('click', resetView);
|
267 |
|
268 |
animate();
|
@@ -288,7 +299,7 @@ html_code = """
|
|
288 |
renderer.render(scene, camera);
|
289 |
}
|
290 |
|
291 |
-
init
|
292 |
</script>
|
293 |
</body>
|
294 |
</html>
|
@@ -297,7 +308,7 @@ html_code = """
|
|
297 |
# Render the HTML component
|
298 |
components.html(html_code, height=600)
|
299 |
|
300 |
-
st.sidebar.title("City Evolution Simulator")
|
301 |
st.sidebar.write("""
|
302 |
## How to Play
|
303 |
|
|
|
1 |
import streamlit as st
|
2 |
import streamlit.components.v1 as components
|
3 |
|
4 |
+
st.set_page_config(page_title="3D City Evolution Simulator", layout="wide")
|
5 |
|
6 |
+
st.title("3D City Evolution Simulator")
|
7 |
st.write("Watch a 3D city grow with lakes, hills, and evolving blocks")
|
8 |
|
9 |
html_code = """
|
|
|
11 |
<html>
|
12 |
<head>
|
13 |
<meta charset="utf-8">
|
14 |
+
<title>3D City Evolution Simulator</title>
|
15 |
<style>
|
16 |
body { margin: 0; overflow: hidden; }
|
17 |
+
#container { width: 100%; height: 100%; }
|
18 |
canvas { width: 100%; height: 100%; display: block; }
|
19 |
.ui-panel {
|
20 |
position: absolute;
|
|
|
41 |
</style>
|
42 |
</head>
|
43 |
<body>
|
44 |
+
<div id="container"></div>
|
45 |
<div class="ui-panel">
|
46 |
<h3>City Controls</h3>
|
47 |
<button id="evolve">Evolve City</button>
|
|
|
91 |
const stack = [];
|
92 |
let position = basePos.clone();
|
93 |
let direction = new THREE.Vector3(0, 1, 0);
|
|
|
94 |
const structure = new THREE.Group();
|
95 |
+
|
96 |
for (let char of this.generate()) {
|
97 |
switch(char) {
|
98 |
case 'F':
|
|
|
133 |
class CitySimulator {
|
134 |
constructor() {
|
135 |
this.blocks = [];
|
136 |
+
this.blockSize = 10;
|
137 |
this.maxBuildingsPerBlock = 5;
|
138 |
this.generation = 0;
|
139 |
this.lakeCenters = [
|
|
|
171 |
|
172 |
const building = lsystem.build(scene, basePos, block.maxHeight);
|
173 |
if (this.isWaterfront(block.position.x, block.position.y)) {
|
174 |
+
building.scale.set(1.5, 2, 1.5);
|
175 |
}
|
176 |
scene.add(building);
|
177 |
block.buildings.push(building);
|
178 |
}
|
179 |
}
|
180 |
|
181 |
+
evolve(scene) {
|
182 |
this.generation++;
|
183 |
if (this.blocks.length < 20) {
|
184 |
const x = (Math.random() - 0.5) * 140;
|
|
|
210 |
let city;
|
211 |
|
212 |
function init() {
|
213 |
+
const container = document.getElementById('container');
|
214 |
+
if (!container) {
|
215 |
+
console.error('Container not found');
|
216 |
+
return;
|
217 |
+
}
|
218 |
+
|
219 |
+
// Scene
|
220 |
scene = new THREE.Scene();
|
221 |
scene.background = new THREE.Color(0x87CEEB);
|
222 |
|
223 |
+
// Camera
|
224 |
camera = new THREE.PerspectiveCamera(75, 16 / 9, 0.1, 1000);
|
225 |
camera.position.set(0, 50, 80);
|
226 |
|
227 |
+
// Renderer
|
228 |
renderer = new THREE.WebGLRenderer({ antialias: true });
|
229 |
renderer.setSize(window.innerWidth, window.innerHeight * (9/16));
|
230 |
container.appendChild(renderer.domElement);
|
231 |
|
232 |
// Lights
|
233 |
+
const ambientLight = new THREE.AmbientLight(0x404040);
|
234 |
+
scene.add(ambientLight);
|
235 |
const sun = new THREE.DirectionalLight(0xffffff, 0.8);
|
236 |
sun.position.set(50, 50, 50);
|
237 |
scene.add(sun);
|
238 |
|
239 |
+
// Ground
|
240 |
const groundGeo = new THREE.PlaneGeometry(160, 90, 32, 32);
|
241 |
const groundMat = new THREE.MeshPhongMaterial({ color: 0x4a7043 });
|
242 |
const ground = new THREE.Mesh(groundGeo, groundMat);
|
|
|
244 |
ground.position.y = -0.1;
|
245 |
scene.add(ground);
|
246 |
|
247 |
+
// Lakes
|
248 |
const lakeGeo = new THREE.CircleGeometry(10, 32);
|
249 |
const lakeMat = new THREE.MeshPhongMaterial({ color: 0x4682b4 });
|
250 |
+
const lakeCenters = [new THREE.Vector2(20, 20), new THREE.Vector2(-30, 10)];
|
251 |
+
lakeCenters.forEach(center => {
|
252 |
const lake = new THREE.Mesh(lakeGeo, lakeMat);
|
253 |
lake.rotation.x = -Math.PI / 2;
|
254 |
+
lake.position.set(center.x, 0.01, center.y);
|
255 |
scene.add(lake);
|
256 |
});
|
257 |
|
258 |
+
// Bridge
|
259 |
const bridgeGeo = new THREE.BoxGeometry(5, 0.2, 15);
|
260 |
const bridgeMat = new THREE.MeshPhongMaterial({ color: 0x808080 });
|
261 |
+
const bridge = new THREE.Mesh(bridgeGeo, bridgeMat);
|
262 |
+
bridge.position.set(15, 0.2, 20);
|
263 |
+
scene.add(bridge);
|
264 |
|
265 |
// Controls
|
266 |
controls = new THREE.OrbitControls(camera, renderer.domElement);
|
267 |
controls.enableDamping = true;
|
268 |
controls.target.set(0, 0, 0);
|
269 |
|
270 |
+
// City
|
271 |
city = new CitySimulator();
|
272 |
city.addBlock(scene, 0, 0);
|
273 |
|
274 |
// Events
|
275 |
window.addEventListener('resize', onWindowResize);
|
276 |
+
document.getElementById('evolve').addEventListener('click', () => city.evolve(scene));
|
277 |
document.getElementById('reset').addEventListener('click', resetView);
|
278 |
|
279 |
animate();
|
|
|
299 |
renderer.render(scene, camera);
|
300 |
}
|
301 |
|
302 |
+
window.onload = init;
|
303 |
</script>
|
304 |
</body>
|
305 |
</html>
|
|
|
308 |
# Render the HTML component
|
309 |
components.html(html_code, height=600)
|
310 |
|
311 |
+
st.sidebar.title("3D City Evolution Simulator")
|
312 |
st.sidebar.write("""
|
313 |
## How to Play
|
314 |
|