awacke1 commited on
Commit
6ea2eb4
Β·
verified Β·
1 Parent(s): 607db4f

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +257 -0
app.py ADDED
@@ -0,0 +1,257 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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 city grow using L-Grammar and Three.js")
8
+
9
+ html_code = """
10
+ <!DOCTYPE html>
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;
20
+ top: 10px;
21
+ right: 10px;
22
+ background: rgba(0,0,0,0.7);
23
+ padding: 15px;
24
+ border-radius: 5px;
25
+ color: white;
26
+ font-family: Arial, sans-serif;
27
+ z-index: 1000;
28
+ }
29
+ .ui-panel button {
30
+ margin: 5px 0;
31
+ padding: 5px 10px;
32
+ width: 100%;
33
+ background: #4CAF50;
34
+ color: white;
35
+ border: none;
36
+ border-radius: 3px;
37
+ cursor: pointer;
38
+ }
39
+ .ui-panel button:hover { background: #45a049; }
40
+ </style>
41
+ </head>
42
+ <body>
43
+ <div class="ui-panel">
44
+ <h3>City Controls</h3>
45
+ <button id="evolve">Evolve City</button>
46
+ <button id="reset">Reset View</button>
47
+ <div id="stats">
48
+ <p>Buildings: <span id="building-count">0</span></p>
49
+ <p>Generation: <span id="generation">0</span></p>
50
+ </div>
51
+ </div>
52
+
53
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r134/three.min.js"></script>
54
+ <script src="https://cdn.jsdelivr.net/npm/[email protected]/examples/js/controls/OrbitControls.js"></script>
55
+ <script>
56
+ class CityLGrammar {
57
+ constructor() {
58
+ this.axiom = "F";
59
+ this.rules = {
60
+ "F": ["F[+F]F[-F]", "F[+F][-F]F", "FF", "F[+F]"],
61
+ "+": ["+"],
62
+ "-": ["-"],
63
+ "[": ["["],
64
+ "]": ["]"]
65
+ };
66
+ this.generation = 0;
67
+ this.angle = Math.PI / 4;
68
+ this.currentString = this.axiom;
69
+ }
70
+
71
+ evolve() {
72
+ this.generation++;
73
+ let result = this.currentString;
74
+ let newString = "";
75
+
76
+ for (let char of result) {
77
+ if (this.rules[char]) {
78
+ const possibleRules = this.rules[char];
79
+ const selectedRule = possibleRules[Math.floor(Math.random() * possibleRules.length)];
80
+ newString += selectedRule;
81
+ } else {
82
+ newString += char;
83
+ }
84
+ }
85
+ this.currentString = newString;
86
+ return this.currentString;
87
+ }
88
+
89
+ buildCity(scene) {
90
+ while(scene.children.length > 2) { // Keep lights
91
+ scene.remove(scene.children[2]);
92
+ }
93
+
94
+ const stack = [];
95
+ let position = new THREE.Vector3(0, 0, 0);
96
+ let direction = new THREE.Vector3(0, 1, 0);
97
+ let buildingCount = 0;
98
+
99
+ // Add ground plane
100
+ const groundGeo = new THREE.PlaneGeometry(100, 100);
101
+ const groundMat = new THREE.MeshPhongMaterial({ color: 0x4a7043 });
102
+ const ground = new THREE.Mesh(groundGeo, groundMat);
103
+ ground.rotation.x = -Math.PI / 2;
104
+ ground.position.y = -0.1;
105
+ scene.add(ground);
106
+
107
+ for (let char of this.currentString) {
108
+ switch(char) {
109
+ case 'F':
110
+ const height = 2 + Math.random() * 6;
111
+ const width = 1 + Math.random() * 2;
112
+
113
+ // Create building
114
+ const buildingGeo = new THREE.BoxGeometry(width, height, width);
115
+ const buildingMat = new THREE.MeshPhongMaterial({
116
+ color: new THREE.Color(0.5 + Math.random() * 0.5,
117
+ 0.5 + Math.random() * 0.5,
118
+ 0.5 + Math.random() * 0.5),
119
+ shininess: 30
120
+ });
121
+ const building = new THREE.Mesh(buildingGeo, buildingMat);
122
+
123
+ building.position.copy(position);
124
+ building.position.y = height / 2;
125
+ building.rotation.y = Math.random() * Math.PI;
126
+ scene.add(building);
127
+
128
+ position.add(direction.clone().multiplyScalar(height));
129
+ buildingCount++;
130
+ break;
131
+
132
+ case '+':
133
+ direction.applyAxisAngle(new THREE.Vector3(0, 0, 1), this.angle);
134
+ break;
135
+
136
+ case '-':
137
+ direction.applyAxisAngle(new THREE.Vector3(0, 0, 1), -this.angle);
138
+ break;
139
+
140
+ case '[':
141
+ stack.push({
142
+ position: position.clone(),
143
+ direction: direction.clone()
144
+ });
145
+ break;
146
+
147
+ case ']':
148
+ if (stack.length > 0) {
149
+ const state = stack.pop();
150
+ position = state.position;
151
+ direction = state.direction;
152
+ }
153
+ break;
154
+ }
155
+ }
156
+
157
+ document.getElementById('building-count').textContent = buildingCount;
158
+ document.getElementById('generation').textContent = this.generation;
159
+ }
160
+ }
161
+
162
+ // Three.js setup
163
+ let scene, camera, renderer, controls;
164
+ let city;
165
+
166
+ function init() {
167
+ const container = document.body;
168
+
169
+ // Scene
170
+ scene = new THREE.Scene();
171
+ scene.background = new THREE.Color(0x87CEEB);
172
+
173
+ // Camera
174
+ camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
175
+ camera.position.set(0, 20, 30);
176
+
177
+ // Renderer
178
+ renderer = new THREE.WebGLRenderer({ antialias: true });
179
+ renderer.setSize(window.innerWidth, window.innerHeight);
180
+ container.appendChild(renderer.domElement);
181
+
182
+ // Lights
183
+ scene.add(new THREE.AmbientLight(0x404040));
184
+ const sun = new THREE.DirectionalLight(0xffffff, 0.8);
185
+ sun.position.set(50, 50, 50);
186
+ scene.add(sun);
187
+
188
+ // Controls
189
+ controls = new THREE.OrbitControls(camera, renderer.domElement);
190
+ controls.enableDamping = true;
191
+ controls.target.set(0, 0, 0);
192
+
193
+ // Initialize city
194
+ city = new CityLGrammar();
195
+ city.buildCity(scene);
196
+
197
+ // Events
198
+ window.addEventListener('resize', onWindowResize);
199
+ document.getElementById('evolve').addEventListener('click', evolveCity);
200
+ document.getElementById('reset').addEventListener('click', resetView);
201
+
202
+ animate();
203
+ }
204
+
205
+ function evolveCity() {
206
+ city.evolve();
207
+ city.buildCity(scene);
208
+ }
209
+
210
+ function resetView() {
211
+ camera.position.set(0, 20, 30);
212
+ controls.target.set(0, 0, 0);
213
+ controls.update();
214
+ }
215
+
216
+ function onWindowResize() {
217
+ camera.aspect = window.innerWidth / window.innerHeight;
218
+ camera.updateProjectionMatrix();
219
+ renderer.setSize(window.innerWidth, window.innerHeight);
220
+ }
221
+
222
+ function animate() {
223
+ requestAnimationFrame(animate);
224
+ controls.update();
225
+ renderer.render(scene, camera);
226
+ }
227
+
228
+ init();
229
+ </script>
230
+ </body>
231
+ </html>
232
+ """
233
+
234
+ # Render the HTML component
235
+ components.html(html_code, height=600)
236
+
237
+ st.sidebar.title("City Evolution Simulator")
238
+ st.sidebar.write("""
239
+ ## How to Play
240
+
241
+ Watch a city evolve through generations using L-System grammar rules.
242
+
243
+ ### Controls:
244
+ - **Evolve City**: Generate next city generation
245
+ - **Reset View**: Return to default view
246
+ - **Left-click + drag**: Rotate view
247
+ - **Right-click + drag**: Pan view
248
+ - **Scroll**: Zoom in/out
249
+
250
+ ### Evolution Rules:
251
+ - Each generation adds more buildings
252
+ - Buildings branch out from existing structures
253
+ - Random heights and sizes create variety
254
+ - Roads implicitly form between buildings
255
+
256
+ Track progress with building count and generation number!
257
+ """)