Initial commit
This commit is contained in:
90
docs/tutorials/basics/async-programming.md
Normal file
90
docs/tutorials/basics/async-programming.md
Normal file
@@ -0,0 +1,90 @@
|
||||
# Async Programming in Vuer
|
||||
|
||||
## Overview
|
||||
|
||||
Vuer supports asynchronous programming patterns for handling parallel routines and callbacks. The tutorial demonstrates creating a server with background tasks running concurrently.
|
||||
|
||||
## Key Components
|
||||
|
||||
### Server Setup
|
||||
The framework uses `Vuer()` to instantiate a server with configuration options via query parameters like `reconnect=True` and `collapseMenu=True`.
|
||||
|
||||
### Main Function Decorator
|
||||
The `@app.spawn(start=True)` decorator marks the entry point as an async function that starts the application immediately.
|
||||
|
||||
### Task Management
|
||||
Sessions provide `spawn_task()` method for launching background operations. Tasks can be cancelled with `.cancel()` when no longer needed.
|
||||
|
||||
## Code Pattern Example
|
||||
|
||||
The tutorial shows a main loop that:
|
||||
- Spawns independent background tasks
|
||||
- Updates scene objects continuously using `sess.upsert`
|
||||
- Manages task lifecycle by cancelling long-running operations after conditions are met
|
||||
- Uses `await sleep()` for non-blocking delays
|
||||
|
||||
## Complete Example
|
||||
|
||||
```python
|
||||
from vuer import Vuer
|
||||
from vuer.schemas import Scene, Box
|
||||
import asyncio
|
||||
import numpy as np
|
||||
|
||||
app = Vuer(
|
||||
queries=dict(
|
||||
reconnect=True,
|
||||
collapseMenu=True,
|
||||
),
|
||||
)
|
||||
|
||||
async def background_task(session):
|
||||
"""A background task that runs independently"""
|
||||
count = 0
|
||||
while True:
|
||||
print(f"Background task running: {count}")
|
||||
count += 1
|
||||
await asyncio.sleep(1.0)
|
||||
|
||||
@app.spawn(start=True)
|
||||
async def main(session):
|
||||
# Spawn a background task
|
||||
task = session.spawn_task(background_task(session))
|
||||
|
||||
# Main animation loop
|
||||
for i in range(100):
|
||||
theta = i * 0.1
|
||||
x = 0.5 * np.cos(theta)
|
||||
z = 0.5 * np.sin(theta)
|
||||
|
||||
# Update the box position
|
||||
session.upsert @ Box(
|
||||
args=[0.1, 0.1, 0.1],
|
||||
position=[x, 0.05, z],
|
||||
color="red",
|
||||
materialType="standard",
|
||||
key="animated-box",
|
||||
)
|
||||
|
||||
await asyncio.sleep(0.05)
|
||||
|
||||
# Cancel the background task when done
|
||||
task.cancel()
|
||||
|
||||
app.run()
|
||||
```
|
||||
|
||||
## Practical Features
|
||||
|
||||
The demonstration animates a red box moving in a circular path while background tasks execute independently, illustrating how multiple asynchronous operations coexist within a single VuerSession without blocking the main rendering loop.
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Use `session.spawn_task()`** for background operations
|
||||
2. **Always use `await asyncio.sleep()`** for delays to avoid blocking
|
||||
3. **Cancel tasks** when they're no longer needed to free resources
|
||||
4. **Use `session.upsert`** for updating scene components
|
||||
|
||||
## Source
|
||||
|
||||
Documentation: https://docs.vuer.ai/en/latest/tutorials/basics/async_programming.html
|
||||
145
docs/tutorials/basics/serving-dynamic-content.md
Normal file
145
docs/tutorials/basics/serving-dynamic-content.md
Normal file
@@ -0,0 +1,145 @@
|
||||
# Serving Dynamic HTML Content in Vuer
|
||||
|
||||
## Overview
|
||||
|
||||
Vuer allows you to serve dynamic content by adding custom route handlers to your application server.
|
||||
|
||||
## Implementation Method
|
||||
|
||||
You can register a dynamic HTML handler using the `add_route()` method.
|
||||
|
||||
## Basic Example
|
||||
|
||||
```python
|
||||
from vuer import Vuer
|
||||
|
||||
app = Vuer()
|
||||
|
||||
counter = 0
|
||||
|
||||
def dynamic_html_handler():
|
||||
global counter
|
||||
counter += 1
|
||||
template = f"""
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head><title>Dynamic HTML</title></head>
|
||||
<body>
|
||||
<h1>Counter Value: {counter}</h1>
|
||||
</body>
|
||||
</html>
|
||||
"""
|
||||
return template
|
||||
|
||||
app.add_route("/dynamic", dynamic_html_handler, method="GET")
|
||||
app.run()
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
After starting the server, visit `http://localhost:8012/dynamic` to see the dynamically generated content. The counter value will update on each page reload.
|
||||
|
||||
## Key Parameters
|
||||
|
||||
- **Route path**: Specify the URL endpoint (e.g., "/dynamic")
|
||||
- **Handler function**: Returns HTML content as a string
|
||||
- **HTTP method**: Specify the request method (e.g., "GET")
|
||||
|
||||
## Advanced Example with JSON Response
|
||||
|
||||
```python
|
||||
from vuer import Vuer
|
||||
import json
|
||||
|
||||
app = Vuer()
|
||||
|
||||
def json_api_handler():
|
||||
data = {
|
||||
"status": "success",
|
||||
"data": {
|
||||
"message": "Hello from Vuer!",
|
||||
"timestamp": time.time()
|
||||
}
|
||||
}
|
||||
return json.dumps(data)
|
||||
|
||||
app.add_route("/api/data", json_api_handler, method="GET")
|
||||
app.run()
|
||||
```
|
||||
|
||||
## Multiple Routes
|
||||
|
||||
You can add multiple routes to your application:
|
||||
|
||||
```python
|
||||
from vuer import Vuer
|
||||
|
||||
app = Vuer()
|
||||
|
||||
def home_handler():
|
||||
return "<h1>Home Page</h1>"
|
||||
|
||||
def about_handler():
|
||||
return "<h1>About Page</h1>"
|
||||
|
||||
def api_handler():
|
||||
return '{"status": "ok"}'
|
||||
|
||||
app.add_route("/", home_handler, method="GET")
|
||||
app.add_route("/about", about_handler, method="GET")
|
||||
app.add_route("/api/status", api_handler, method="GET")
|
||||
|
||||
app.run()
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Return strings**: Handler functions should return HTML or text as strings
|
||||
2. **Use templates**: Consider using template engines for complex HTML
|
||||
3. **Handle errors**: Add error handling in your route handlers
|
||||
4. **Set content types**: For JSON responses, consider setting appropriate headers
|
||||
|
||||
## Combining with 3D Scenes
|
||||
|
||||
You can serve both dynamic HTML content and 3D scenes from the same Vuer application:
|
||||
|
||||
```python
|
||||
from vuer import Vuer
|
||||
from vuer.schemas import Scene, Box
|
||||
import asyncio
|
||||
|
||||
app = Vuer()
|
||||
|
||||
# Add dynamic HTML route
|
||||
def stats_handler():
|
||||
return f"""
|
||||
<html>
|
||||
<body>
|
||||
<h1>Vuer Statistics</h1>
|
||||
<p>Server running on port 8012</p>
|
||||
</body>
|
||||
</html>
|
||||
"""
|
||||
|
||||
app.add_route("/stats", stats_handler, method="GET")
|
||||
|
||||
# Add 3D scene
|
||||
@app.spawn
|
||||
async def main(session):
|
||||
session.set @ Scene(
|
||||
Box(
|
||||
args=[0.1, 0.1, 0.1],
|
||||
position=[0, 0, 0],
|
||||
key="box",
|
||||
),
|
||||
)
|
||||
|
||||
while True:
|
||||
await asyncio.sleep(1.0)
|
||||
|
||||
app.run()
|
||||
```
|
||||
|
||||
## Source
|
||||
|
||||
Documentation: https://docs.vuer.ai/en/latest/tutorials/basics/adding_html_handler.html
|
||||
111
docs/tutorials/basics/setting-a-scene.md
Normal file
111
docs/tutorials/basics/setting-a-scene.md
Normal file
@@ -0,0 +1,111 @@
|
||||
# Setting Up Your First Scene in Vuer
|
||||
|
||||
## Overview
|
||||
This tutorial guides you through creating a basic 3D scene using Vuer, a Python framework for building interactive 3D visualizations. The example demonstrates how to establish a server, add scene components, lighting, and interactive elements.
|
||||
|
||||
## Step 1: Initialize the Vuer Server
|
||||
|
||||
Begin by importing and instantiating the application:
|
||||
|
||||
```python
|
||||
from vuer import Vuer
|
||||
|
||||
app = Vuer(
|
||||
queries=dict(
|
||||
reconnect=True,
|
||||
collapseMenu=True,
|
||||
),
|
||||
)
|
||||
```
|
||||
|
||||
The queries parameter configures the scene via URL parameters. Launch the server with `app.run()`, which produces output showing the local connection URL.
|
||||
|
||||
## Step 2: Create an Async Session
|
||||
|
||||
The framework uses WebSocket sessions to connect clients with the Python server. Bind an async function to handle each session using the spawn decorator:
|
||||
|
||||
```python
|
||||
@app.spawn
|
||||
async def session(sess: VuerSession):
|
||||
print("Example: we have started a websocket session!")
|
||||
```
|
||||
|
||||
## Step 3: Build Your Scene
|
||||
|
||||
Within the session, construct the scene by setting Scene objects containing various components:
|
||||
|
||||
```python
|
||||
sess.set @ Scene(
|
||||
Box(
|
||||
args=[0.1, 0.1, 0.1, 101, 101, 101],
|
||||
position=[0, 0.05, 0],
|
||||
color="red",
|
||||
materialType="standard",
|
||||
material=dict(color="#23aaff"),
|
||||
key="fox-1",
|
||||
),
|
||||
...
|
||||
)
|
||||
```
|
||||
|
||||
## Key Components
|
||||
|
||||
### Box
|
||||
A 3D cube primitive with position, color, and material properties.
|
||||
|
||||
### SpotLight
|
||||
Provides directional lighting within the scene, wrapped in a Movable component to allow interactive repositioning.
|
||||
|
||||
### Movable
|
||||
Enables user interaction, allowing scene elements to be dragged and manipulated in the 3D viewport.
|
||||
|
||||
## Essential Pattern
|
||||
|
||||
Always include `await asyncio.sleep(0.0)` after scene modifications to ensure proper asynchronous handling and client synchronization.
|
||||
|
||||
## Complete Example
|
||||
|
||||
```python
|
||||
from vuer import Vuer
|
||||
from vuer.schemas import Scene, Box, SpotLight, Movable
|
||||
import asyncio
|
||||
|
||||
app = Vuer(
|
||||
queries=dict(
|
||||
reconnect=True,
|
||||
collapseMenu=True,
|
||||
),
|
||||
)
|
||||
|
||||
@app.spawn
|
||||
async def session(sess):
|
||||
print("Example: we have started a websocket session!")
|
||||
|
||||
sess.set @ Scene(
|
||||
Box(
|
||||
args=[0.1, 0.1, 0.1, 101, 101, 101],
|
||||
position=[0, 0.05, 0],
|
||||
color="red",
|
||||
materialType="standard",
|
||||
material=dict(color="#23aaff"),
|
||||
key="fox-1",
|
||||
),
|
||||
Movable(
|
||||
SpotLight(
|
||||
intensity=3.0,
|
||||
distance=10.0,
|
||||
decay=0.0,
|
||||
position=[0, 2, 0],
|
||||
key="spotlight",
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
await asyncio.sleep(0.0)
|
||||
|
||||
app.run()
|
||||
```
|
||||
|
||||
## Source
|
||||
|
||||
Documentation: https://docs.vuer.ai/en/latest/tutorials/basics/setting_a_scene.html
|
||||
102
docs/tutorials/basics/simple-life-cycle.md
Normal file
102
docs/tutorials/basics/simple-life-cycle.md
Normal file
@@ -0,0 +1,102 @@
|
||||
# Vuer Component Life Cycle
|
||||
|
||||
## Core Concept
|
||||
|
||||
This tutorial demonstrates the basic **CRUD operations** for components in Vuer: adding a component, updating in-place, and removing.
|
||||
|
||||
## Life Cycle Operations
|
||||
|
||||
### 1. Adding Components
|
||||
Create new components using `session.upsert`:
|
||||
|
||||
```python
|
||||
session.upsert @ Obj(
|
||||
src="/static/model.obj",
|
||||
position=[0, 0, 0],
|
||||
key="my-model",
|
||||
)
|
||||
```
|
||||
|
||||
### 2. Updating Components
|
||||
Modify existing components by reusing the same key:
|
||||
|
||||
```python
|
||||
session.upsert @ Obj(
|
||||
src="/static/model.obj",
|
||||
position=[1, 0, 0], # Changed position
|
||||
key="my-model", # Same key updates the existing component
|
||||
)
|
||||
```
|
||||
|
||||
### 3. Removing Components
|
||||
Delete components with `session.remove`:
|
||||
|
||||
```python
|
||||
session.remove @ "my-model"
|
||||
```
|
||||
|
||||
## Complete Example
|
||||
|
||||
```python
|
||||
from vuer import Vuer
|
||||
from vuer.schemas import Obj
|
||||
import asyncio
|
||||
|
||||
app = Vuer()
|
||||
|
||||
@app.spawn
|
||||
async def main(session):
|
||||
# Create/Update: Add a wireframe mesh
|
||||
for i in range(80):
|
||||
wireframe = i % 2 == 0
|
||||
|
||||
# Toggle wireframe on and off
|
||||
session.upsert @ Obj(
|
||||
src="/static/model.obj",
|
||||
position=[0, 0, 0],
|
||||
wireframe=wireframe,
|
||||
key="primary-mesh",
|
||||
)
|
||||
|
||||
await asyncio.sleep(0.01)
|
||||
|
||||
# Add a second component
|
||||
session.upsert @ Obj(
|
||||
src="/static/model.obj",
|
||||
position=[1, 0, 0],
|
||||
wireframe=True,
|
||||
key="secondary-mesh",
|
||||
)
|
||||
|
||||
await asyncio.sleep(0.8)
|
||||
|
||||
# Remove the second component
|
||||
session.remove @ "secondary-mesh"
|
||||
|
||||
# Keep the session alive
|
||||
while True:
|
||||
await asyncio.sleep(1.0)
|
||||
|
||||
app.run()
|
||||
```
|
||||
|
||||
## Key Patterns
|
||||
|
||||
### Using Keys
|
||||
- **Unique keys** identify components in the scene
|
||||
- **Reusing a key** updates the existing component
|
||||
- **Different keys** create new components
|
||||
|
||||
### Update Frequency
|
||||
The example toggles wireframe every 0.01 seconds (100 times per second), demonstrating how Vuer handles rapid updates efficiently.
|
||||
|
||||
### Timing
|
||||
Components can be added and removed at any time during the session, allowing for dynamic scene management.
|
||||
|
||||
## Visual Effect
|
||||
|
||||
This creates an animated effect showing two mesh versions (solid and wireframe) alternating visibility, demonstrating how components can be dynamically managed throughout an application's runtime.
|
||||
|
||||
## Source
|
||||
|
||||
Documentation: https://docs.vuer.ai/en/latest/tutorials/basics/simple_life_cycle.html
|
||||
121
docs/tutorials/basics/ssl-proxy-webxr.md
Normal file
121
docs/tutorials/basics/ssl-proxy-webxr.md
Normal file
@@ -0,0 +1,121 @@
|
||||
# Setting Up SSL Proxy for WebXR
|
||||
|
||||
## Overview
|
||||
|
||||
Vuer requires secure connections for WebXR functionality. Both the web client and WebSocket connections must use TLS/SSL encryption.
|
||||
|
||||
## Key Configuration Points
|
||||
|
||||
### WebSocket Endpoint Setup
|
||||
Pass the secure WebSocket URL via query parameter to the web client:
|
||||
```
|
||||
https://vuer.ai?ws=wss://xxxxx.ngrok.io
|
||||
```
|
||||
|
||||
### Static File Serving
|
||||
Update component source paths to use the correct HTTPS domain. For example, change:
|
||||
|
||||
```python
|
||||
# Before (insecure)
|
||||
src='http://localhost:8012/static/urdf/robot.urdf'
|
||||
```
|
||||
|
||||
To:
|
||||
|
||||
```python
|
||||
# After (secure)
|
||||
src='https://<your-domain-with-ssl>/static/urdf/robot.urdf'
|
||||
```
|
||||
|
||||
## Recommended Proxy Solutions
|
||||
|
||||
### Option 1: ngrok (Recommended)
|
||||
|
||||
ngrok converts local HTTP/WebSocket connections to secure HTTPS/WSS:
|
||||
|
||||
- `ws://localhost:8012` → `wss://xxxx.ngrok.io`
|
||||
- `http://localhost:8012/static/` → `https://xxxx.ngrok.io/static/`
|
||||
|
||||
**Installation:**
|
||||
Visit [ngrok's website](https://ngrok.com) for installation instructions.
|
||||
|
||||
**Usage:**
|
||||
```bash
|
||||
ngrok http 8012
|
||||
```
|
||||
|
||||
This will provide a secure URL like `https://xxxx.ngrok.io` that you can use for WebXR.
|
||||
|
||||
### Option 2: localtunnel
|
||||
|
||||
Free alternative requiring a passcode.
|
||||
|
||||
**Installation:**
|
||||
```bash
|
||||
npm install -g localtunnel
|
||||
```
|
||||
|
||||
**Usage:**
|
||||
```bash
|
||||
lt --port 8012
|
||||
```
|
||||
|
||||
**Documentation:** https://localtunnel.me
|
||||
|
||||
### Option 3: Let's Encrypt Self-Signed Certificate
|
||||
|
||||
Generate and use your own SSL certificate.
|
||||
|
||||
**Launch Vuer with Certificate:**
|
||||
```bash
|
||||
vuer --cert cert.pem --key key.pem --port 8012
|
||||
```
|
||||
|
||||
**Generate Certificate:**
|
||||
Follow Let's Encrypt's localhost certificate guide for implementation details.
|
||||
|
||||
## Complete Example with ngrok
|
||||
|
||||
```python
|
||||
from vuer import Vuer
|
||||
from vuer.schemas import Scene, Urdf
|
||||
|
||||
app = Vuer()
|
||||
|
||||
@app.spawn
|
||||
async def main(session):
|
||||
# Use the ngrok HTTPS domain for static files
|
||||
session.set @ Scene(
|
||||
Urdf(
|
||||
src='https://xxxx.ngrok.io/static/urdf/robot.urdf',
|
||||
position=[0, 0, 0],
|
||||
key="robot",
|
||||
),
|
||||
)
|
||||
|
||||
while True:
|
||||
await asyncio.sleep(1.0)
|
||||
|
||||
app.run()
|
||||
```
|
||||
|
||||
Then access your app at:
|
||||
```
|
||||
https://vuer.ai?ws=wss://xxxx.ngrok.io
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### WebSocket Connection Fails
|
||||
- Ensure you're using `wss://` (not `ws://`)
|
||||
- Verify the ngrok tunnel is running
|
||||
- Check firewall settings
|
||||
|
||||
### Static Files Not Loading
|
||||
- Confirm HTTPS domain is correct
|
||||
- Verify static files are being served
|
||||
- Check browser console for mixed content warnings
|
||||
|
||||
## Source
|
||||
|
||||
Documentation: https://docs.vuer.ai/en/latest/tutorials/basics/ssl_proxy_webxr.html
|
||||
Reference in New Issue
Block a user