Spaces:
Running
Running
import streamlit as st | |
import ast | |
import base64 | |
import streamlit.components.v1 as components | |
from transformers import pipeline | |
from gtts import gTTS | |
import qrcode | |
from io import BytesIO | |
st.set_page_config(page_title="AR/VR Code Visualizer", layout="wide") | |
st.title("π AR/VR Code Visualizer with Interaction, Narration, and AR View") | |
def load_model(): | |
return pipeline("summarization", model="philschmid/bart-large-cnn-samsum") | |
summarizer = load_model() | |
st.subheader("π Write or Paste Your Python Code") | |
code = st.text_area("Enter your Python code here", height=300) | |
if code.strip(): | |
st.code(code, language="python") | |
# AST parsing to get function call relationships | |
tree = ast.parse(code) | |
class FunctionCallVisitor(ast.NodeVisitor): | |
def __init__(self): | |
self.calls = {} | |
def visit_FunctionDef(self, node): | |
caller = node.name | |
self.calls[caller] = [] | |
for child in ast.walk(node): | |
if isinstance(child, ast.Call) and isinstance(child.func, ast.Name): | |
self.calls[caller].append(child.func.id) | |
self.generic_visit(node) | |
visitor = FunctionCallVisitor() | |
visitor.visit(tree) | |
call_graph = visitor.calls | |
st.subheader("π Function Calls") | |
for fn, callees in call_graph.items(): | |
st.write(f"πΉ `{fn}` calls: {', '.join(callees) if callees else 'None'}") | |
# AI summary | |
prompt = f"Explain this call graph structure: {call_graph}" | |
summary = summarizer(prompt, max_length=60, min_length=15, do_sample=False) | |
summary_text = summary[0]['summary_text'] | |
st.success(summary_text) | |
# Voice narration | |
st.subheader("π Voice Narration") | |
tts = gTTS(text=summary_text) | |
tts.save("summary.mp3") | |
st.audio("summary.mp3", format="audio/mp3") | |
# A-Frame 3D HTML generator | |
def generate_aframe(call_graph): | |
function_data = { | |
"functions": [], | |
"relationships": [] | |
} | |
function_positions = {} | |
spacing = 3 | |
for i, fn in enumerate(call_graph): | |
x = i * spacing | |
function_positions[fn] = (x, 1, -3) | |
function_data["functions"].append({"name": fn, "position": [x, 1, -3]}) | |
for caller, callees in call_graph.items(): | |
for callee in callees: | |
if callee in function_positions: | |
x1, y1, z1 = function_positions[caller] | |
x2, y2, z2 = function_positions[callee] | |
function_data["relationships"].append({"start": [x1, y1, z1], "end": [x2, y2, z2]}) | |
js = f""" | |
<script> | |
const functionData = {str(function_data).replace("'", '"')}; | |
function say(text) {{ | |
const utter = new SpeechSynthesisUtterance(text); | |
speechSynthesis.speak(utter); | |
}} | |
document.addEventListener('DOMContentLoaded', () => {{ | |
functionData.functions.forEach(function(fn) {{ | |
const box = document.createElement('a-box'); | |
box.setAttribute('position', fn.position.join(' ')); | |
box.setAttribute('depth', '0.5'); | |
box.setAttribute('height', '0.5'); | |
box.setAttribute('width', '2'); | |
box.setAttribute('color', '#FFC65D'); | |
box.setAttribute('class', 'clickable'); | |
box.setAttribute('event-set__enter', '_event: mouseenter; color: #00CED1'); | |
box.setAttribute('event-set__leave', '_event: mouseleave; color: #FFC65D'); | |
box.setAttribute('onclick', 'say(\"' + fn.name + '\")'); | |
document.querySelector('a-scene').appendChild(box); | |
const text = document.createElement('a-text'); | |
text.setAttribute('value', fn.name); | |
text.setAttribute('position', `${{fn.position[0]}} ${{fn.position[1] + 1}} ${{fn.position[2]}}`); | |
text.setAttribute('align', 'center'); | |
text.setAttribute('color', '#000'); | |
document.querySelector('a-scene').appendChild(text); | |
}}); | |
functionData.relationships.forEach(function(rel) {{ | |
const el = document.createElement('a-entity'); | |
el.setAttribute('line', `start: ${{rel.start.join(' ')}}; end: ${{rel.end.join(' ')}}; color: red`); | |
document.querySelector('a-scene').appendChild(el); | |
}}); | |
}}); | |
</script> | |
""" | |
html = f""" | |
<!DOCTYPE html> | |
<html> | |
<head> | |
<meta charset="utf-8"> | |
<script src="https://aframe.io/releases/1.3.0/aframe.min.js"></script> | |
<script src="https://cdn.jsdelivr.net/gh/donmccurdy/aframe-extras@6.1.1/dist/aframe-extras.min.js"></script> | |
<script src="https://unpkg.com/aframe-screenshot-component@3.3.0/dist/aframe-screenshot-component.min.js"></script> | |
{js} | |
</head> | |
<body> | |
<a-scene screenshot> | |
<a-entity position="0 1.6 3"> | |
<a-camera wasd-controls-enabled="true" look-controls-enabled="true"></a-camera> | |
</a-entity> | |
<a-light type="ambient" color="#FFF"></a-light> | |
<a-plane rotation="-90 0 0" width="40" height="40" color="#7BC8A4"></a-plane> | |
</a-scene> | |
</body> | |
</html> | |
""" | |
return html | |
aframe_html = generate_aframe(call_graph) | |
b64 = base64.b64encode(aframe_html.encode()).decode() | |
data_url = f"data:text/html;base64,{b64}" | |
st.subheader("π Interactive 3D Function Visualizer") | |
components.iframe(data_url, height=600) | |
# QR Code | |
st.subheader("π± AR View on Mobile") | |
qr = qrcode.QRCode(version=1, box_size=10, border=5) | |
space_url = "https://huggingface.co/spaces/your-username/your-space-name" # Replace this | |
qr.add_data(space_url) | |
qr.make(fit=True) | |
img = qr.make_image(fill_color="black", back_color="white") | |
img_byte_arr = BytesIO() | |
img.save(img_byte_arr, format='PNG') | |
img_bytes = img_byte_arr.getvalue() | |
st.image(img_bytes, caption="Scan to view in AR on mobile") | |
else: | |
st.info("Write some Python code above to visualize, narrate, and explore it in 3D/AR.") | |