awacke1 commited on
Commit
1a55305
Β·
verified Β·
1 Parent(s): 85e35de

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +103 -92
app.py CHANGED
@@ -6,17 +6,27 @@ st.set_page_config(page_title="3D City Evolution Simulator", layout="wide")
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 = """
 
 
 
 
 
 
 
 
 
 
10
  <!DOCTYPE html>
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;
21
  top: 10px;
22
  right: 10px;
@@ -26,8 +36,8 @@ html_code = """
26
  color: white;
27
  font-family: Arial, sans-serif;
28
  z-index: 1000;
29
- }
30
- .ui-panel button {
31
  margin: 5px 0;
32
  padding: 5px 10px;
33
  width: 100%;
@@ -36,8 +46,8 @@ html_code = """
36
  border: none;
37
  border-radius: 3px;
38
  cursor: pointer;
39
- }
40
- .ui-panel button:hover { background: #45a049; }
41
  </style>
42
  </head>
43
  <body>
@@ -56,37 +66,37 @@ html_code = """
56
  <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r134/three.min.js"></script>
57
  <script src="https://cdn.jsdelivr.net/npm/[email protected]/examples/js/controls/OrbitControls.js"></script>
58
  <script>
59
- class BuildingLSystem {
60
- constructor() {
61
  this.axiom = "F";
62
- this.rules = {
63
  "F": ["F[+F]", "F[-F]", "FF", "F"],
64
  "+": ["+"],
65
  "-": ["-"],
66
  "[": ["["],
67
  "]": ["]"]
68
- };
69
  this.angle = Math.PI / 6;
70
- }
71
 
72
- generate() {
73
  let result = this.axiom;
74
- for (let i = 0; i < 2; i++) {
75
  let newString = "";
76
- for (let char of result) {
77
- if (this.rules[char]) {
78
  const possible = this.rules[char];
79
  newString += possible[Math.floor(Math.random() * possible.length)];
80
- } else {
81
  newString += char;
82
- }
83
- }
84
  result = newString;
85
- }
86
  return result;
87
- }
88
 
89
- build(scene, basePos, maxHeight) {
90
  let height = 0;
91
  const stack = [];
92
  let position = basePos.clone();
@@ -94,24 +104,24 @@ html_code = """
94
  const structure = new THREE.Group();
95
  let baseWidth = 1.5; // Starting wider base
96
 
97
- for (let char of this.generate()) {
98
- switch(char) {
99
  case 'F':
100
- if (height < maxHeight) {
101
  const width = baseWidth * (1 - height / maxHeight); // Narrower as it goes up
102
  const floorHeight = 2 + Math.random() * 2; // Taller floors
103
  const geo = new THREE.BoxGeometry(width, floorHeight, width);
104
- const mat = new THREE.MeshPhongMaterial({
105
  color: new THREE.Color(0.5 + Math.random() * 0.5,
106
  0.5 + Math.random() * 0.5,
107
  0.5 + Math.random() * 0.5)
108
- });
109
  const floor = new THREE.Mesh(geo, mat);
110
  floor.position.copy(position).add(new THREE.Vector3(0, floorHeight/2, 0));
111
  structure.add(floor);
112
  position.y += floorHeight;
113
  height += floorHeight;
114
- }
115
  break;
116
  case '+':
117
  direction.applyAxisAngle(new THREE.Vector3(0, 0, 1), this.angle);
@@ -125,14 +135,14 @@ html_code = """
125
  case ']':
126
  if (stack.length > 0) position = stack.pop();
127
  break;
128
- }
129
- }
130
  return structure;
131
- }
132
- }
133
 
134
- class CitySimulator {
135
- constructor() {
136
  this.blocks = [];
137
  this.blockSize = 10;
138
  this.maxBuildingsPerBlock = 5;
@@ -141,26 +151,26 @@ html_code = """
141
  new THREE.Vector2(20, 20),
142
  new THREE.Vector2(-30, 10)
143
  ];
144
- }
145
 
146
- addBlock(scene, x, z) {
147
- const block = {
148
  position: new THREE.Vector2(x, z),
149
  buildings: [],
150
- maxHeight: this.isWaterfront(x, z) ? 20 : 12 // Increased max height
151
- };
152
  this.blocks.push(block);
153
  this.evolveBlock(scene, block, true);
154
- }
155
 
156
- isWaterfront(x, z) {
157
  const pos = new THREE.Vector2(x, z);
158
  return this.lakeCenters.some(center =>
159
  pos.distanceTo(center) < 15 && pos.distanceTo(center) > 5);
160
- }
161
 
162
- evolveBlock(scene, block, initial = false) {
163
- if (block.buildings.length < this.maxBuildingsPerBlock) {
164
  const lsystem = new BuildingLSystem();
165
  const gridX = Math.floor(Math.random() * 3) - 1;
166
  const gridZ = Math.floor(Math.random() * 3) - 1;
@@ -171,63 +181,63 @@ html_code = """
171
  );
172
 
173
  const building = lsystem.build(scene, basePos, block.maxHeight);
174
- if (this.isWaterfront(block.position.x, block.position.y)) {
175
  building.scale.set(1.5, 2, 1.5);
176
- }
177
  scene.add(building);
178
  block.buildings.push(building);
179
- }
180
- }
181
 
182
- evolve(scene) {
183
  this.generation++;
184
- if (this.blocks.length < 20) {
185
- const x = (Math.random() - 0.5) * 80; // Adjusted for 9:16
186
- const z = (Math.random() - 0.5) * 140;
187
  if (!this.isInLake(x, z)) this.addBlock(scene, x, z);
188
- }
189
  this.blocks.forEach(block => this.evolveBlock(scene, block));
190
  this.updateStats();
191
- }
192
 
193
- getTerrainHeight(x, z) {
194
  return Math.sin(x * 0.05) * Math.cos(z * 0.05) * 5;
195
- }
196
 
197
- isInLake(x, z) {
198
  const pos = new THREE.Vector2(x, z);
199
  return this.lakeCenters.some(center => pos.distanceTo(center) < 10);
200
- }
201
 
202
- updateStats() {
203
  const totalBuildings = this.blocks.reduce((sum, block) => sum + block.buildings.length, 0);
204
  document.getElementById('building-count').textContent = totalBuildings;
205
  document.getElementById('block-count').textContent = this.blocks.length;
206
  document.getElementById('generation').textContent = this.generation;
207
- }
208
- }
209
 
210
  let scene, camera, renderer, controls;
211
  let city;
212
 
213
- function init() {
214
  const container = document.getElementById('container');
215
- if (!container) {
216
  console.error('Container not found');
217
  return;
218
- }
219
 
220
  // Scene
221
  scene = new THREE.Scene();
222
  scene.background = new THREE.Color(0x87CEEB);
223
 
224
- // Camera with 9:16 portrait aspect ratio
225
- camera = new THREE.PerspectiveCamera(75, 9 / 16, 0.1, 1000);
226
- camera.position.set(0, 80, 40); // Adjusted for portrait view
227
 
228
  // Renderer
229
- renderer = new THREE.WebGLRenderer({ antialias: true });
230
- renderer.setSize(window.innerWidth, window.innerHeight * (16/9));
231
  container.appendChild(renderer.domElement);
232
 
233
  // Lights
@@ -237,9 +247,9 @@ html_code = """
237
  sun.position.set(50, 50, 50);
238
  scene.add(sun);
239
 
240
- // Ground with 9:16 ratio (90x160)
241
- const groundGeo = new THREE.PlaneGeometry(90, 160, 32, 32);
242
- const groundMat = new THREE.MeshPhongMaterial({ color: 0x4a7043 });
243
  const ground = new THREE.Mesh(groundGeo, groundMat);
244
  ground.rotation.x = -Math.PI / 2;
245
  ground.position.y = -0.1;
@@ -247,18 +257,18 @@ html_code = """
247
 
248
  // Lakes
249
  const lakeGeo = new THREE.CircleGeometry(10, 32);
250
- const lakeMat = new THREE.MeshPhongMaterial({ color: 0x4682b4 });
251
  const lakeCenters = [new THREE.Vector2(20, 20), new THREE.Vector2(-30, 10)];
252
- lakeCenters.forEach(center => {
253
  const lake = new THREE.Mesh(lakeGeo, lakeMat);
254
  lake.rotation.x = -Math.PI / 2;
255
  lake.position.set(center.x, 0.01, center.y);
256
  scene.add(lake);
257
- });
258
 
259
  // Bridge
260
  const bridgeGeo = new THREE.BoxGeometry(5, 0.2, 15);
261
- const bridgeMat = new THREE.MeshPhongMaterial({ color: 0x808080 });
262
  const bridge = new THREE.Mesh(bridgeGeo, bridgeMat);
263
  bridge.position.set(15, 0.2, 20);
264
  scene.add(bridge);
@@ -278,27 +288,27 @@ html_code = """
278
  document.getElementById('reset').addEventListener('click', resetView);
279
 
280
  animate();
281
- }
282
 
283
- function resetView() {
284
- camera.position.set(0, 80, 40); // Adjusted for portrait view
285
  controls.target.set(0, 0, 0);
286
  controls.update();
287
- }
288
 
289
- function onWindowResize() {
290
- const width = window.innerWidth;
291
- const height = width * (16/9); // Portrait orientation
292
- camera.aspect = 9 / 16;
293
  camera.updateProjectionMatrix();
294
  renderer.setSize(width, height);
295
- }
296
 
297
- function animate() {
298
  requestAnimationFrame(animate);
299
  controls.update();
300
  renderer.render(scene, camera);
301
- }
302
 
303
  window.onload = init;
304
  </script>
@@ -306,8 +316,8 @@ html_code = """
306
  </html>
307
  """
308
 
309
- # Render the HTML component
310
- components.html(html_code, height=600)
311
 
312
  st.sidebar.title("3D City Evolution Simulator")
313
  st.sidebar.write("""
@@ -321,9 +331,10 @@ Watch a 3D city evolve with lakes, hills, and building blocks.
321
  - **Left-click + drag**: Rotate
322
  - **Right-click + drag**: Pan
323
  - **Scroll**: Zoom
 
324
 
325
  ### Features:
326
- - 9:16 portrait play area
327
  - Blocks (10x10 units) with up to 5 buildings
328
  - Buildings start wide, grow taller with smaller floors
329
  - Terrain with hills and lakes
 
6
  st.title("3D City Evolution Simulator")
7
  st.write("Watch a 3D city grow with lakes, hills, and evolving blocks")
8
 
9
+ # Sliders for container size with initial 3:4 aspect ratio
10
+ max_width = min(1200, st.session_state.get('window_width', 1200)) # Use a reasonable max or screen width
11
+ max_height = min(1600, st.session_state.get('window_height', 1600)) # Use a reasonable max or screen height
12
+
13
+ col1, col2 = st.columns(2)
14
+ with col1:
15
+ container_width = st.slider("Container Width (px)", 300, max_width, 768, step=50)
16
+ with col2:
17
+ container_height = st.slider("Container Height (px)", 400, max_height, 1024, step=50)
18
+
19
+ html_code = f"""
20
  <!DOCTYPE html>
21
  <html>
22
  <head>
23
  <meta charset="utf-8">
24
  <title>3D City Evolution Simulator</title>
25
  <style>
26
+ body {{ margin: 0; overflow: hidden; }}
27
+ #container {{ width: {container_width}px; height: {container_height}px; margin: 0 auto; }}
28
+ canvas {{ width: 100%; height: 100%; display: block; }}
29
+ .ui-panel {{
30
  position: absolute;
31
  top: 10px;
32
  right: 10px;
 
36
  color: white;
37
  font-family: Arial, sans-serif;
38
  z-index: 1000;
39
+ }}
40
+ .ui-panel button {{
41
  margin: 5px 0;
42
  padding: 5px 10px;
43
  width: 100%;
 
46
  border: none;
47
  border-radius: 3px;
48
  cursor: pointer;
49
+ }}
50
+ .ui-panel button:hover {{ background: #45a049; }}
51
  </style>
52
  </head>
53
  <body>
 
66
  <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r134/three.min.js"></script>
67
  <script src="https://cdn.jsdelivr.net/npm/[email protected]/examples/js/controls/OrbitControls.js"></script>
68
  <script>
69
+ class BuildingLSystem {{
70
+ constructor() {{
71
  this.axiom = "F";
72
+ this.rules = {{
73
  "F": ["F[+F]", "F[-F]", "FF", "F"],
74
  "+": ["+"],
75
  "-": ["-"],
76
  "[": ["["],
77
  "]": ["]"]
78
+ }};
79
  this.angle = Math.PI / 6;
80
+ }}
81
 
82
+ generate() {{
83
  let result = this.axiom;
84
+ for (let i = 0; i < 2; i++) {{
85
  let newString = "";
86
+ for (let char of result) {{
87
+ if (this.rules[char]) {{
88
  const possible = this.rules[char];
89
  newString += possible[Math.floor(Math.random() * possible.length)];
90
+ }} else {{
91
  newString += char;
92
+ }}
93
+ }}
94
  result = newString;
95
+ }}
96
  return result;
97
+ }}
98
 
99
+ build(scene, basePos, maxHeight) {{
100
  let height = 0;
101
  const stack = [];
102
  let position = basePos.clone();
 
104
  const structure = new THREE.Group();
105
  let baseWidth = 1.5; // Starting wider base
106
 
107
+ for (let char of this.generate()) {{
108
+ switch(char) {{
109
  case 'F':
110
+ if (height < maxHeight) {{
111
  const width = baseWidth * (1 - height / maxHeight); // Narrower as it goes up
112
  const floorHeight = 2 + Math.random() * 2; // Taller floors
113
  const geo = new THREE.BoxGeometry(width, floorHeight, width);
114
+ const mat = new THREE.MeshPhongMaterial({{
115
  color: new THREE.Color(0.5 + Math.random() * 0.5,
116
  0.5 + Math.random() * 0.5,
117
  0.5 + Math.random() * 0.5)
118
+ }});
119
  const floor = new THREE.Mesh(geo, mat);
120
  floor.position.copy(position).add(new THREE.Vector3(0, floorHeight/2, 0));
121
  structure.add(floor);
122
  position.y += floorHeight;
123
  height += floorHeight;
124
+ }}
125
  break;
126
  case '+':
127
  direction.applyAxisAngle(new THREE.Vector3(0, 0, 1), this.angle);
 
135
  case ']':
136
  if (stack.length > 0) position = stack.pop();
137
  break;
138
+ }}
139
+ }}
140
  return structure;
141
+ }}
142
+ }}
143
 
144
+ class CitySimulator {{
145
+ constructor() {{
146
  this.blocks = [];
147
  this.blockSize = 10;
148
  this.maxBuildingsPerBlock = 5;
 
151
  new THREE.Vector2(20, 20),
152
  new THREE.Vector2(-30, 10)
153
  ];
154
+ }}
155
 
156
+ addBlock(scene, x, z) {{
157
+ const block = {{
158
  position: new THREE.Vector2(x, z),
159
  buildings: [],
160
+ maxHeight: this.isWaterfront(x, z) ? 20 : 12
161
+ }};
162
  this.blocks.push(block);
163
  this.evolveBlock(scene, block, true);
164
+ }}
165
 
166
+ isWaterfront(x, z) {{
167
  const pos = new THREE.Vector2(x, z);
168
  return this.lakeCenters.some(center =>
169
  pos.distanceTo(center) < 15 && pos.distanceTo(center) > 5);
170
+ }}
171
 
172
+ evolveBlock(scene, block, initial = false) {{
173
+ if (block.buildings.length < this.maxBuildingsPerBlock) {{
174
  const lsystem = new BuildingLSystem();
175
  const gridX = Math.floor(Math.random() * 3) - 1;
176
  const gridZ = Math.floor(Math.random() * 3) - 1;
 
181
  );
182
 
183
  const building = lsystem.build(scene, basePos, block.maxHeight);
184
+ if (this.isWaterfront(block.position.x, block.position.y)) {{
185
  building.scale.set(1.5, 2, 1.5);
186
+ }}
187
  scene.add(building);
188
  block.buildings.push(building);
189
+ }}
190
+ }}
191
 
192
+ evolve(scene) {{
193
  this.generation++;
194
+ if (this.blocks.length < 20) {{
195
+ const x = (Math.random() - 0.5) * 90; // Adjusted for 3:4
196
+ const z = (Math.random() - 0.5) * 120;
197
  if (!this.isInLake(x, z)) this.addBlock(scene, x, z);
198
+ }}
199
  this.blocks.forEach(block => this.evolveBlock(scene, block));
200
  this.updateStats();
201
+ }}
202
 
203
+ getTerrainHeight(x, z) {{
204
  return Math.sin(x * 0.05) * Math.cos(z * 0.05) * 5;
205
+ }}
206
 
207
+ isInLake(x, z) {{
208
  const pos = new THREE.Vector2(x, z);
209
  return this.lakeCenters.some(center => pos.distanceTo(center) < 10);
210
+ }}
211
 
212
+ updateStats() {{
213
  const totalBuildings = this.blocks.reduce((sum, block) => sum + block.buildings.length, 0);
214
  document.getElementById('building-count').textContent = totalBuildings;
215
  document.getElementById('block-count').textContent = this.blocks.length;
216
  document.getElementById('generation').textContent = this.generation;
217
+ }}
218
+ }}
219
 
220
  let scene, camera, renderer, controls;
221
  let city;
222
 
223
+ function init() {{
224
  const container = document.getElementById('container');
225
+ if (!container) {{
226
  console.error('Container not found');
227
  return;
228
+ }}
229
 
230
  // Scene
231
  scene = new THREE.Scene();
232
  scene.background = new THREE.Color(0x87CEEB);
233
 
234
+ // Camera with 3:4 aspect ratio
235
+ camera = new THREE.PerspectiveCamera(75, 3 / 4, 0.1, 1000);
236
+ camera.position.set(0, 50, 60);
237
 
238
  // Renderer
239
+ renderer = new THREE.WebGLRenderer({{ antialias: true }});
240
+ renderer.setSize({container_width}, {container_height});
241
  container.appendChild(renderer.domElement);
242
 
243
  // Lights
 
247
  sun.position.set(50, 50, 50);
248
  scene.add(sun);
249
 
250
+ // Ground with 3:4 ratio (90x120)
251
+ const groundGeo = new THREE.PlaneGeometry(90, 120, 32, 32);
252
+ const groundMat = new THREE.MeshPhongMaterial({{ color: 0x4a7043 }});
253
  const ground = new THREE.Mesh(groundGeo, groundMat);
254
  ground.rotation.x = -Math.PI / 2;
255
  ground.position.y = -0.1;
 
257
 
258
  // Lakes
259
  const lakeGeo = new THREE.CircleGeometry(10, 32);
260
+ const lakeMat = new THREE.MeshPhongMaterial({{ color: 0x4682b4 }});
261
  const lakeCenters = [new THREE.Vector2(20, 20), new THREE.Vector2(-30, 10)];
262
+ lakeCenters.forEach(center => {{
263
  const lake = new THREE.Mesh(lakeGeo, lakeMat);
264
  lake.rotation.x = -Math.PI / 2;
265
  lake.position.set(center.x, 0.01, center.y);
266
  scene.add(lake);
267
+ }});
268
 
269
  // Bridge
270
  const bridgeGeo = new THREE.BoxGeometry(5, 0.2, 15);
271
+ const bridgeMat = new THREE.MeshPhongMaterial({{ color: 0x808080 }});
272
  const bridge = new THREE.Mesh(bridgeGeo, bridgeMat);
273
  bridge.position.set(15, 0.2, 20);
274
  scene.add(bridge);
 
288
  document.getElementById('reset').addEventListener('click', resetView);
289
 
290
  animate();
291
+ }}
292
 
293
+ function resetView() {{
294
+ camera.position.set(0, 50, 60);
295
  controls.target.set(0, 0, 0);
296
  controls.update();
297
+ }}
298
 
299
+ function onWindowResize() {{
300
+ const width = {container_width};
301
+ const height = {container_height};
302
+ camera.aspect = 3 / 4;
303
  camera.updateProjectionMatrix();
304
  renderer.setSize(width, height);
305
+ }}
306
 
307
+ function animate() {{
308
  requestAnimationFrame(animate);
309
  controls.update();
310
  renderer.render(scene, camera);
311
+ }}
312
 
313
  window.onload = init;
314
  </script>
 
316
  </html>
317
  """
318
 
319
+ # Render the HTML component with dynamic size
320
+ components.html(html_code, width=container_width, height=container_height)
321
 
322
  st.sidebar.title("3D City Evolution Simulator")
323
  st.sidebar.write("""
 
331
  - **Left-click + drag**: Rotate
332
  - **Right-click + drag**: Pan
333
  - **Scroll**: Zoom
334
+ - **Sliders**: Adjust play area size
335
 
336
  ### Features:
337
+ - 3:4 initial play area (768x1024)
338
  - Blocks (10x10 units) with up to 5 buildings
339
  - Buildings start wide, grow taller with smaller floors
340
  - Terrain with hills and lakes