awacke1 commited on
Commit
921b54d
Β·
verified Β·
1 Parent(s): b6ac696

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +517 -0
app.py ADDED
@@ -0,0 +1,517 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ import streamlit.components.v1 as components
3
+
4
+ st.set_page_config(page_title="L-Grammar 3D Assembly Game", layout="wide")
5
+
6
+ st.title("L-Grammar 3D Assembly Game")
7
+ st.write("An interactive 3D game using L-Grammar to assemble primitive components")
8
+
9
+ # Create a custom HTML component to embed Three.js
10
+ html_code = """
11
+ <!DOCTYPE html>
12
+ <html>
13
+ <head>
14
+ <meta charset="utf-8">
15
+ <title>L-Grammar 3D Assemblies</title>
16
+ <style>
17
+ body { margin: 0; overflow: hidden; }
18
+ canvas { display: block; }
19
+ .ui-panel {
20
+ position: absolute;
21
+ top: 10px;
22
+ right: 10px;
23
+ background: rgba(0,0,0,0.6);
24
+ padding: 10px;
25
+ border-radius: 5px;
26
+ color: white;
27
+ font-family: Arial, sans-serif;
28
+ }
29
+ .ui-panel button {
30
+ margin: 5px;
31
+ padding: 5px 10px;
32
+ background: #555;
33
+ color: white;
34
+ border: none;
35
+ border-radius: 3px;
36
+ cursor: pointer;
37
+ }
38
+ .ui-panel button:hover {
39
+ background: #777;
40
+ }
41
+ </style>
42
+ </head>
43
+ <body>
44
+ <div class="ui-panel">
45
+ <h3>L-Grammar Controls</h3>
46
+ <div id="rules"></div>
47
+ <button id="generate">Generate New Assembly</button>
48
+ <button id="reset">Reset View</button>
49
+ <div id="stats">
50
+ <p>Parts: <span id="parts-count">0</span></p>
51
+ <p>Complexity: <span id="complexity">0</span></p>
52
+ </div>
53
+ </div>
54
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
55
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.7.7/dat.gui.min.js"></script>
56
+ <script>
57
+ // L-Grammar System for 3D Assemblies
58
+ class LGrammarSystem {
59
+ constructor() {
60
+ this.axiom = "F";
61
+ this.rules = {
62
+ "F": ["F[+F]F", "F[-F]F", "F[+F][-F]F", "FF"],
63
+ "+": ["+", "++"],
64
+ "-": ["-", "--"],
65
+ "[": ["["],
66
+ "]": ["]"]
67
+ };
68
+ this.angle = Math.PI / 6;
69
+ this.iterations = 3;
70
+ this.currentString = this.axiom;
71
+ }
72
+
73
+ generate() {
74
+ let result = this.axiom;
75
+ for (let i = 0; i < this.iterations; i++) {
76
+ let newString = "";
77
+ for (let j = 0; j < result.length; j++) {
78
+ const char = result[j];
79
+ if (this.rules[char]) {
80
+ const possibleRules = this.rules[char];
81
+ const selectedRule = possibleRules[Math.floor(Math.random() * possibleRules.length)];
82
+ newString += selectedRule;
83
+ } else {
84
+ newString += char;
85
+ }
86
+ }
87
+ result = newString;
88
+ }
89
+ this.currentString = result;
90
+ return result;
91
+ }
92
+
93
+ interpret(scene) {
94
+ const stack = [];
95
+ let position = new THREE.Vector3(0, 0, 0);
96
+ let direction = new THREE.Vector3(0, 1, 0);
97
+ let right = new THREE.Vector3(1, 0, 0);
98
+ let up = new THREE.Vector3(0, 0, 1);
99
+
100
+ // Clear previous objects
101
+ while(scene.children.length > 0) {
102
+ const object = scene.children[0];
103
+ if (object.type === "DirectionalLight" ||
104
+ object.type === "AmbientLight" ||
105
+ object.type === "PointLight") {
106
+ scene.children.shift();
107
+ } else {
108
+ scene.remove(object);
109
+ }
110
+ }
111
+
112
+ let partCount = 0;
113
+
114
+ for (let i = 0; i < this.currentString.length; i++) {
115
+ const char = this.currentString[i];
116
+
117
+ switch(char) {
118
+ case 'F':
119
+ // Create a part (cylinder or box)
120
+ const partType = Math.random() > 0.5 ? 'cylinder' : 'box';
121
+ const length = 2 + Math.random() * 3;
122
+ const width = 0.3 + Math.random() * 0.5;
123
+
124
+ let geometry, material, part;
125
+
126
+ if (partType === 'cylinder') {
127
+ geometry = new THREE.CylinderGeometry(width, width, length, 8);
128
+ material = new THREE.MeshPhongMaterial({
129
+ color: new THREE.Color(Math.random(), Math.random(), Math.random()),
130
+ shininess: 30
131
+ });
132
+ part = new THREE.Mesh(geometry, material);
133
+ } else {
134
+ geometry = new THREE.BoxGeometry(width, length, width);
135
+ material = new THREE.MeshPhongMaterial({
136
+ color: new THREE.Color(Math.random(), Math.random(), Math.random()),
137
+ shininess: 30
138
+ });
139
+ part = new THREE.Mesh(geometry, material);
140
+ }
141
+
142
+ // Position and orient the part
143
+ const midPoint = position.clone().add(direction.clone().multiplyScalar(length/2));
144
+ part.position.copy(midPoint);
145
+
146
+ // Calculate the rotation to align with direction
147
+ const defaultDir = new THREE.Vector3(0, 1, 0);
148
+ part.quaternion.setFromUnitVectors(defaultDir, direction.clone().normalize());
149
+
150
+ scene.add(part);
151
+ partCount++;
152
+
153
+ // Move forward
154
+ position.add(direction.clone().multiplyScalar(length));
155
+ break;
156
+
157
+ case '+':
158
+ // Rotate right around the up vector
159
+ const rotationMatrixPlus = new THREE.Matrix4().makeRotationAxis(up, this.angle);
160
+ direction.applyMatrix4(rotationMatrixPlus);
161
+ right.applyMatrix4(rotationMatrixPlus);
162
+ break;
163
+
164
+ case '-':
165
+ // Rotate left around the up vector
166
+ const rotationMatrixMinus = new THREE.Matrix4().makeRotationAxis(up, -this.angle);
167
+ direction.applyMatrix4(rotationMatrixMinus);
168
+ right.applyMatrix4(rotationMatrixMinus);
169
+ break;
170
+
171
+ case '&':
172
+ // Pitch down around the right vector
173
+ const rotationMatrixPitchDown = new THREE.Matrix4().makeRotationAxis(right, this.angle);
174
+ direction.applyMatrix4(rotationMatrixPitchDown);
175
+ up.applyMatrix4(rotationMatrixPitchDown);
176
+ break;
177
+
178
+ case '^':
179
+ // Pitch up around the right vector
180
+ const rotationMatrixPitchUp = new THREE.Matrix4().makeRotationAxis(right, -this.angle);
181
+ direction.applyMatrix4(rotationMatrixPitchUp);
182
+ up.applyMatrix4(rotationMatrixPitchUp);
183
+ break;
184
+
185
+ case '\\':
186
+ // Roll clockwise around forward vector
187
+ const rotationMatrixRollCW = new THREE.Matrix4().makeRotationAxis(direction, this.angle);
188
+ right.applyMatrix4(rotationMatrixRollCW);
189
+ up.applyMatrix4(rotationMatrixRollCW);
190
+ break;
191
+
192
+ case '/':
193
+ // Roll counter-clockwise around forward vector
194
+ const rotationMatrixRollCCW = new THREE.Matrix4().makeRotationAxis(direction, -this.angle);
195
+ right.applyMatrix4(rotationMatrixRollCCW);
196
+ up.applyMatrix4(rotationMatrixRollCCW);
197
+ break;
198
+
199
+ case '[':
200
+ // Push current state onto stack
201
+ stack.push({
202
+ position: position.clone(),
203
+ direction: direction.clone(),
204
+ right: right.clone(),
205
+ up: up.clone()
206
+ });
207
+ break;
208
+
209
+ case ']':
210
+ // Pop state from stack
211
+ if (stack.length > 0) {
212
+ const state = stack.pop();
213
+ position = state.position;
214
+ direction = state.direction;
215
+ right = state.right;
216
+ up = state.up;
217
+ }
218
+ break;
219
+ }
220
+ }
221
+
222
+ // Create a connector at each joint
223
+ for (let i = 0; i < stack.length; i++) {
224
+ const jointPosition = stack[i].position;
225
+ const jointGeometry = new THREE.SphereGeometry(0.5, 8, 8);
226
+ const jointMaterial = new THREE.MeshPhongMaterial({
227
+ color: 0xFFD700,
228
+ shininess: 50
229
+ });
230
+ const joint = new THREE.Mesh(jointGeometry, jointMaterial);
231
+ joint.position.copy(jointPosition);
232
+ scene.add(joint);
233
+ partCount++;
234
+ }
235
+
236
+ document.getElementById('parts-count').textContent = partCount;
237
+ document.getElementById('complexity').textContent = this.currentString.length;
238
+
239
+ return partCount;
240
+ }
241
+ }
242
+
243
+ // Three.js setup
244
+ let scene, camera, renderer;
245
+ let lgrammar;
246
+ let controls;
247
+
248
+ function init() {
249
+ // Scene setup
250
+ scene = new THREE.Scene();
251
+ scene.background = new THREE.Color(0x111122);
252
+
253
+ // Camera setup
254
+ const width = window.innerWidth;
255
+ const height = window.innerHeight;
256
+ camera = new THREE.PerspectiveCamera(75, width / height, 0.1, 1000);
257
+ camera.position.set(0, 0, 30);
258
+ camera.lookAt(0, 0, 0);
259
+
260
+ // Renderer setup
261
+ renderer = new THREE.WebGLRenderer({ antialias: true });
262
+ renderer.setSize(width, height);
263
+ renderer.setPixelRatio(window.devicePixelRatio);
264
+ document.body.appendChild(renderer.domElement);
265
+
266
+ // Lighting
267
+ const ambientLight = new THREE.AmbientLight(0x404040);
268
+ scene.add(ambientLight);
269
+
270
+ const directionalLight = new THREE.DirectionalLight(0xffffff, 0.8);
271
+ directionalLight.position.set(1, 1, 1);
272
+ scene.add(directionalLight);
273
+
274
+ const pointLight = new THREE.PointLight(0xffffff, 0.5);
275
+ pointLight.position.set(-10, 10, 10);
276
+ scene.add(pointLight);
277
+
278
+ // OrbitControls
279
+ controls = new THREE.OrbitControls(camera, renderer.domElement);
280
+ controls.enableDamping = true;
281
+ controls.dampingFactor = 0.05;
282
+
283
+ // Initialize L-Grammar system
284
+ lgrammar = new LGrammarSystem();
285
+ generateNewAssembly();
286
+
287
+ // Event listeners
288
+ window.addEventListener('resize', onWindowResize);
289
+ document.getElementById('generate').addEventListener('click', generateNewAssembly);
290
+ document.getElementById('reset').addEventListener('click', resetView);
291
+
292
+ // Start animation loop
293
+ animate();
294
+ }
295
+
296
+ function generateNewAssembly() {
297
+ lgrammar.iterations = Math.floor(2 + Math.random() * 3);
298
+ lgrammar.angle = (Math.PI / 8) + (Math.random() * Math.PI / 4);
299
+ lgrammar.generate();
300
+ lgrammar.interpret(scene);
301
+ resetView();
302
+ }
303
+
304
+ function resetView() {
305
+ camera.position.set(0, 0, 30);
306
+ camera.lookAt(0, 0, 0);
307
+ controls.reset();
308
+ }
309
+
310
+ function onWindowResize() {
311
+ camera.aspect = window.innerWidth / window.innerHeight;
312
+ camera.updateProjectionMatrix();
313
+ renderer.setSize(window.innerWidth, window.innerHeight);
314
+ }
315
+
316
+ function animate() {
317
+ requestAnimationFrame(animate);
318
+ controls.update();
319
+ renderer.render(scene, camera);
320
+ }
321
+
322
+ // Add OrbitControls (simplified version)
323
+ THREE.OrbitControls = function(camera, domElement) {
324
+ this.camera = camera;
325
+ this.domElement = domElement;
326
+ this.enableDamping = false;
327
+ this.dampingFactor = 0.05;
328
+
329
+ // API
330
+ this.target = new THREE.Vector3();
331
+
332
+ // Internal state
333
+ this.rotateStart = new THREE.Vector2();
334
+ this.rotateEnd = new THREE.Vector2();
335
+ this.rotateDelta = new THREE.Vector2();
336
+
337
+ this.panStart = new THREE.Vector2();
338
+ this.panEnd = new THREE.Vector2();
339
+ this.panDelta = new THREE.Vector2();
340
+
341
+ this.dollyStart = new THREE.Vector2();
342
+ this.dollyEnd = new THREE.Vector2();
343
+ this.dollyDelta = new THREE.Vector2();
344
+
345
+ this.state = {
346
+ NONE: -1,
347
+ ROTATE: 0,
348
+ DOLLY: 1,
349
+ PAN: 2
350
+ };
351
+ this.currentState = this.state.NONE;
352
+
353
+ // Set up event listeners
354
+ this.domElement.addEventListener('mousedown', onMouseDown.bind(this));
355
+ this.domElement.addEventListener('mousemove', onMouseMove.bind(this));
356
+ this.domElement.addEventListener('mouseup', onMouseUp.bind(this));
357
+ this.domElement.addEventListener('wheel', onMouseWheel.bind(this));
358
+
359
+ function onMouseDown(event) {
360
+ event.preventDefault();
361
+
362
+ if (event.button === 0) {
363
+ this.currentState = this.state.ROTATE;
364
+ this.rotateStart.set(event.clientX, event.clientY);
365
+ } else if (event.button === 1) {
366
+ this.currentState = this.state.DOLLY;
367
+ this.dollyStart.set(event.clientX, event.clientY);
368
+ } else if (event.button === 2) {
369
+ this.currentState = this.state.PAN;
370
+ this.panStart.set(event.clientX, event.clientY);
371
+ }
372
+ }
373
+
374
+ function onMouseMove(event) {
375
+ event.preventDefault();
376
+
377
+ if (this.currentState === this.state.ROTATE) {
378
+ this.rotateEnd.set(event.clientX, event.clientY);
379
+ this.rotateDelta.subVectors(this.rotateEnd, this.rotateStart);
380
+
381
+ const element = this.domElement;
382
+
383
+ // Rotate
384
+ const rotSpeed = 0.002;
385
+ const thetaX = 2 * Math.PI * this.rotateDelta.x / element.clientWidth * rotSpeed;
386
+ const thetaY = 2 * Math.PI * this.rotateDelta.y / element.clientHeight * rotSpeed;
387
+
388
+ // Calculate camera position relative to target
389
+ const offset = new THREE.Vector3().subVectors(this.camera.position, this.target);
390
+
391
+ // Rotate around target
392
+ const qx = new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(0, 1, 0), thetaX);
393
+ offset.applyQuaternion(qx);
394
+
395
+ const qy = new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(1, 0, 0), thetaY);
396
+ offset.applyQuaternion(qy);
397
+
398
+ // Update camera position
399
+ this.camera.position.copy(this.target).add(offset);
400
+ this.camera.lookAt(this.target);
401
+
402
+ this.rotateStart.copy(this.rotateEnd);
403
+
404
+ } else if (this.currentState === this.state.DOLLY) {
405
+ this.dollyEnd.set(event.clientX, event.clientY);
406
+ this.dollyDelta.subVectors(this.dollyEnd, this.dollyStart);
407
+
408
+ // Zoom speed
409
+ const zoomSpeed = 0.01;
410
+
411
+ // Calculate zoom factor
412
+ const factor = 1.0 + this.dollyDelta.y * zoomSpeed;
413
+
414
+ // Apply zoom
415
+ const offset = new THREE.Vector3().subVectors(this.camera.position, this.target);
416
+ offset.multiplyScalar(factor);
417
+ this.camera.position.copy(this.target).add(offset);
418
+
419
+ this.dollyStart.copy(this.dollyEnd);
420
+
421
+ } else if (this.currentState === this.state.PAN) {
422
+ this.panEnd.set(event.clientX, event.clientY);
423
+ this.panDelta.subVectors(this.panEnd, this.panStart);
424
+
425
+ // Pan speed
426
+ const panSpeed = 0.001;
427
+
428
+ // Calculate pan offset
429
+ const distance = this.camera.position.distanceTo(this.target);
430
+ const panX = -this.panDelta.x * distance * panSpeed;
431
+ const panY = this.panDelta.y * distance * panSpeed;
432
+
433
+ // Pan camera
434
+ const v = new THREE.Vector3();
435
+ v.copy(this.camera.position).sub(this.target);
436
+ v.cross(this.camera.up).normalize().multiplyScalar(panX);
437
+ const vpan = new THREE.Vector3().copy(this.camera.up).normalize().multiplyScalar(panY);
438
+ v.add(vpan);
439
+
440
+ this.camera.position.add(v);
441
+ this.target.add(v);
442
+
443
+ this.panStart.copy(this.panEnd);
444
+ }
445
+ }
446
+
447
+ function onMouseUp(event) {
448
+ event.preventDefault();
449
+ this.currentState = this.state.NONE;
450
+ }
451
+
452
+ function onMouseWheel(event) {
453
+ event.preventDefault();
454
+
455
+ // Zoom speed
456
+ const zoomSpeed = 0.05;
457
+
458
+ // Calculate zoom factor (based on scroll direction)
459
+ const delta = Math.sign(event.deltaY);
460
+ const factor = 1.0 - delta * zoomSpeed;
461
+
462
+ // Apply zoom
463
+ const offset = new THREE.Vector3().subVectors(this.camera.position, this.target);
464
+ offset.multiplyScalar(factor);
465
+ this.camera.position.copy(this.target).add(offset);
466
+ }
467
+
468
+ this.update = function() {
469
+ // For damping, not implemented in this simplified version
470
+ };
471
+
472
+ this.reset = function() {
473
+ this.target.set(0, 0, 0);
474
+ this.camera.position.set(0, 0, 30);
475
+ this.camera.lookAt(this.target);
476
+ };
477
+ };
478
+
479
+ // Initialize the application
480
+ init();
481
+ </script>
482
+ </body>
483
+ </html>
484
+ """
485
+
486
+ # Display the Three.js application in an iframe
487
+ components.html(html_code, height=800)
488
+
489
+ st.sidebar.title("Game Instructions")
490
+ st.sidebar.write("""
491
+ ## L-Grammar 3D Assembly Game
492
+
493
+ This game uses L-system grammars to procedurally generate 3D assemblies of parts.
494
+
495
+ ### How it works:
496
+ 1. The system starts with a simple axiom and applies transformation rules iteratively
497
+ 2. The resulting string of characters defines the 3D structure
498
+ 3. Parts are created and connected based on these rules
499
+
500
+ ### Controls:
501
+ - **Left-click + drag**: Rotate the view
502
+ - **Right-click + drag**: Pan the view
503
+ - **Mouse wheel**: Zoom in/out
504
+ - **Generate New Assembly**: Creates a new random structure
505
+ - **Reset View**: Returns to the default camera position
506
+
507
+ ### L-Grammar Commands:
508
+ - F: Move forward and create a part
509
+ - +/-: Rotate left/right
510
+ - [: Push current state onto stack (branch)
511
+ - ]: Pop state from stack (end branch)
512
+
513
+ Have fun exploring the procedurally generated 3D structures!
514
+ """)
515
+
516
+ st.sidebar.markdown("---")
517
+ st.sidebar.write("Made with Three.js and Streamlit")