Files
wireless-docs/docs/ARCHITECTURE.md
2026-09-10 14:09:25 -04:00

14 KiB
Raw Permalink Blame History

Architecture Documentation

This document explains the architecture of the EID Wireless documentation system and its interactive diorama renderer.


Overview

The EID Wireless documentation is a text-first technical resource that uses interactive 3D visualizations (dioramas) as supplemental learning tools. The core principle is: readers should be able to understand the concepts without interactives, but gain deeper insight through them.

This is achieved through a hybrid architecture:

  1. Typst generates static HTML documents from .typ source files
  2. Custom JavaScript renderer provides interactive dioramas embedded in those pages
  3. Vite bundles everything together for development and production

The dioramas are not the primary content—they're visual aids that appear after conceptual explanations and serve to reinforce understanding through visualization.


Architecture Goals

Primary Goal: Text-First Documentation

The documentation system prioritizes readable, searchable, accessible text content. Interactive dioramas:

  • Are optional and degrade gracefully (pages work without JavaScript)
  • Are out-of-the-way (don't interfere with reading flow)
  • Are supplemental (don't contain core information that isn't in text)
  • Are mobile-friendly (scrolling behavior is preserved by default)

Secondary Goals

  1. Accurate Minecraft rendering - Dioramas must faithfully represent block states, rotations, and redstone behavior
  2. Efficient rendering - Multiple dioramas can appear on a single page; rendering must be performant
  3. Easy authoring - Content creators should focus on concepts, not graphics programming
  4. Version independence - Documentation should work across different Minecraft versions

System Architecture

High-Level Flow

Typst Source (.typ files) ──▶ HTML + JavaScript ──▶ Browser
                                     │
                                     └─▶ Interactive Dioramas (WebGL)

Components

1. Typst Document Generator

Role: Converts .typ source files into HTML pages

Key Files:

  • main.typ - Main entry point, defines document structure
  • lib.typ - Library functions (diorama(), callout(), etc.)
  • content/*.typ - Documentation chapters
  • typst.toml - Package metadata

Responsibilities:

  • Compile .typ files into HTML using tinymist language server
  • Embed interactive elements via #show directives
  • Generate navigation between pages

Key Pattern - Diorama Embedding:

#show raw.where(lang: "mc-diorama"): it => diorama(
  orbit: true,
  pan: true,
  autoplay: true,
  it.text,
)

This directive tells Typst to:

  1. Find all mc-diorama code blocks
  2. Wrap them in the diorama() function
  3. Pass block content as the world definition

2. World Definition System

Role: Defines Minecraft scenes using a timeline-based domain-specific language

Format:

p x y z block_type properties
t time

Where:

  • p places or modifies blocks at coordinates
  • t advances the timeline
  • Properties are key=value pairs (e.g., facing=east, powered=true)

Example:

p 0 0 0 dropper facing=up triggered=false
p 0 1 0 dropper facing=up triggered=false
t 2
p 0 0 0 triggered=true
t 4
p 0 0 0 triggered=false

This format is designed to be:

  • Human-readable - Easy to write and debug
  • Concise - Minimal boilerplate
  • Timeline-aware - Changes occur at specific game ticks

3. WebGL Renderer (Engine)

Role: Renders Minecraft blocks as 3D models using WebGL

Key Files:

  • src/engine.js - Main renderer class
  • src/world.js - World state management
  • src/diorama.js - Camera and view management
  • src/math.js - Matrix operations (orthographic projection, look-at, etc.)

Architecture:

Engine (singleton)
├── Dioramas (multiple per page)
│   ├── Canvas (2D context for offscreen rendering)
│   └── Frame (world timeline state)
└── Resources (blockstates, models, textures)
    ├── Cache (async resource loading)
    ├── Blockstate Handler (parses JSON blockstate definitions)
    ├── Model Handler (builds geometry from models)
    └── Texture Handler (atlas textures into single canvas)

Key Features:

  • Instanced rendering - Thousands of blocks rendered efficiently
  • Lazy updates - Only re-renders when content changes
  • View culling - Skips rendering for off-screen dioramas
  • Texture atlasing - Packs all textures into single texture for batch rendering

4. Controller System

Role: Handles user interaction with dioramas

Key Files:

  • src/controller.js - SpacetimeController class
  • src/components.js - Custom HTML elements

Features:

  • Orbit controls - Rotate view (left drag)
  • Pan controls - Move view (middle drag or shift+left)
  • Zoom controls - Scale view (right drag or ctrl+left)
  • Timeline autoplay - Auto-play animations with configurable speed
  • Mobile support - Touch gestures with scroll delegation
  • State preservation - Camera position and timeline state can be reset

Mobile-Specific Behavior:

  • Default browser scroll is preserved (diagram doesn't capture scroll)
  • Touch interaction requires deliberate engagement (200ms timeout)
  • 1-finger touch: orbit (don't interfere with scroll)
  • 2-finger touch: pan + zoom (pinch gesture)

5. Build System

Role: Integrates Typst output with JavaScript rendering

Key Files:

  • vite.config.js - Vite configuration
  • mise.toml - Development tool configuration
  • dist/ - Production output directory

Workflow:

# Development
mise run dev  # Runs Vite dev server + Typst watch

# Build
mise run build  # Compile Typst + bundle with Vite

Vite Configuration:

  • root: .typ-bundle - Typst-generated HTML is the Vite root
  • publicDir: public - Static assets served from Minecraft assets
  • Alias /src → src/ - JavaScript modules accessible from HTML
  • Watch mode monitors both .typ and src/**/* files

Data Flow

1. Document Compilation

Typst Source (.typ)
    ↓ (tinymist)
HTML + Scripts
    ↓ (Vite bundling)
dist/index.html

2. World Parsing

mc-diorama Code Block
    ↓ (World constructor)
World Object
├── events[] - Timeline events (changes at each tick)
├── initialState - Block state at t=0
└── labels - Named timeline positions

3. Frame Playback

World Object
    ↓ (WorldFrame)
Frame Object
├── blocks[] - Current block state
├── currentTime - Timeline position
└── listeners[] - Callbacks on state change

4. Rendering Pipeline

Frame State Change
    ↓
Engine.updateAll()
├── Parse blocks → Blockstates → Models
├── Build geometry (positions, normals, UVs)
├── Update texture atlas
└── Create instanced meshes (VBOs)

Frame State Change (again)
    ↓
Engine.render()
├── Check visibility (cull off-screen dioramas)
├── Update camera matrices (orthographic projection)
├── Draw instanced meshes (WebGL)
└── Copy to 2D canvas (for HTML display)

5. Resource Loading

Request Blockstate/Model/Texture
    ↓
Cache.getAsync()
├── Check if already loading → return promise
├── Check if already loaded → return data
└── Kick off fetch + prepare fallback

Fetch Complete
    ↓
Handler.process()
├── Parse JSON (blockstate/model)
├── Build geometry (model)
└── Return structured data
    ↓
Cache.onResolve() → Engine.requestUpdate()

User Experience: What Users Actually See

Dioramas appear as interactive 3D diagrams within the documentation:

┌─────────────────────────────────────┐
│  Text explanation of redstone...    │
│  (this is the primary content)      │
├─────────────────────────────────────┤
│  ┌───────────────────────────────┐  │
│  │ [Diorama: 3D interactive      │  │
│  │  view of the mechanism]       │  │
│  │                               │  │
│  │  (click/drag to interact)     │  │
│  └───────────────────────────────┘  │
│  Caption: "Figure 1: Mechanism"     │
└─────────────────────────────────────┘

Key UX Principles:

  1. Dioramas are after the text - Readers understand conceptually first, then visualize
  2. Dioramas don't steal scroll - Default browser scroll works; interaction requires deliberate action
  3. Mobile-first interaction - Touch gestures don't interfere with page navigation
  4. Progressive enhancement - Pages work without JS; dioramas add value when JS is available

Camera System

Dioramas use orthographic projection (not perspective) because:

  1. Redstone mechanisms are small and detailed
  2. Orthographic preserves proportions
  3. Better for technical diagrams
  4. Easier to measure distances

Camera Parameters:

  • target - Point camera looks at (world coordinates)
  • radius - Distance from target
  • theta - Horizontal rotation angle (degrees)
  • phi - Vertical rotation angle (degrees)

Matrix Calculation:

  1. Convert spherical coordinates to Cartesian (eye position)
  2. Calculate view direction and up vector
  3. Build view matrix via mat4LookAt()
  4. Build orthographic projection matrix based on radius and aspect ratio
  5. Combine: viewProj = projection × view

Instanced Rendering

Each unique block variant gets its own instanced mesh:

Geometry (positions, normals, UVs, tints, shades)
    ↓
Instance Data
├── Matrix[] (16 floats each) - World transformation
└── Color[] (3 floats each) - Redstone power tint
    ↓
GPU Buffer (VBO)
    ↓
DrawElementsInstanced()

This allows thousands of blocks to be rendered in a single draw call.

Texture Atlasing

All block textures are packed into a single canvas:

16×16px blocks
    ↓
Atlas Canvas (256×256px default)
├── Cell 0: block_0.png
├── Cell 1: block_1.png
├── Cell 2: block_2.png
└── ...
    ↓
UV Map
├── u, v (cell position)
└── du, dv (cell size)

Texture loading is asynchronous but synchronous fallback is provided:

  • Renderer immediately gets UV coordinates
  • Texture data loads in background
  • Fallback texture (black silhouette) shown until loaded

Interactive Element Design

Accessibility First

Dioramas must work without JavaScript:

<!-- Without JS: plain text world definition -->
<mc-diorama>
  <script type="text/mc-world">
    p 0 0 0 dropper...
  </script>
</mc-diorama>

<!-- With JS: interactive canvas -->
<mc-diorama>
  <canvas width="800" height="600"></canvas>
</mc-diorama>

Performance Optimizations

  1. Lazy rendering - Only update when content changes
  2. Visibility culling - Skip off-screen dioramas
  3. State preservation - Only rebuild meshes when blocks change
  4. Texture batching - Single texture bind for all blocks
  5. Instanced drawing - One draw call per block type

Mobile-First Interaction

Touch Handling:

  • 200ms delay before engaging (allows scroll detection)
  • 1-finger: orbit (doesn't interfere with page scroll)
  • 2-finger: pan + zoom (pinch gesture)
  • Touch doesn't block page scroll unless clearly interacting

Scroll Delegation:

canvas.addEventListener('touchstart', e => {
  // Check if user is trying to scroll
  if (maxTouchDistance < 10px) {
    // Let browser handle scroll
    return;
  }
  // Engage diorama interaction
  e.preventDefault();
});

Extensibility

Adding New Block Types

  1. Blockstate JSON - Define block variants in public/assets/minecraft/blockstates/
  2. Model JSON - Define geometry in public/assets/minecraft/models/
  3. Texture PNG - Add texture image in public/assets/minecraft/textures/

The system automatically discovers and loads these assets.

Adding New Diorama Features

The controller system is designed for easy extension:

class SpacetimeController {
  constructor(diorama, options = {}) {
    this.opts = {orbit: true, pan: true, zoom: true, ...options};
    // Add new options here
  }
}

New features can be added as optional behaviors without breaking existing dioramas.


Development Workflow

Running Locally

mise run dev
# Opens browser at localhost:5173
# Automatically reloads on changes to .typ or src/**/*

Building for Production

mise run build
# Compiles Typst to .typ-bundle/
# Bundles with Vite to dist/

Debugging

Enable debug redraw:

window.mcEngine.debug_redraw = true;
// Highlights regions that are being redrawn

Check resource loading:

window.mcEngine.cache.data
// Shows all loaded resources

Inspect frame state:

const frame = document.querySelector('mc-diorama').frame;
console.log(frame.blocks, frame.currentTime);

Future Architecture Changes

The JavaScript API is intentionally unstable. Planned changes:

  1. Component system - Move from custom elements to framework components
  2. WebAssembly rendering - Potential Rust/WASM renderer for better performance
  3. TypeScript migration - Improve type safety and IDE support
  4. Animation system - More sophisticated timeline interpolation
  5. Shader effects - Enhanced visual feedback (glows, particles, etc.)

These changes won't break the Typst authoring experience—the diorama content format will remain the same.


References