summaryrefslogtreecommitdiff
path: root/web-timeplot/ARCHITECTURE.md
diff options
context:
space:
mode:
authorgrothedev <grothedev@gmail.com>2025-11-25 21:22:17 -0500
committergrothedev <grothedev@gmail.com>2025-11-25 21:22:17 -0500
commit43420f2987b76aa7ede0012e1998ba8d61419bc9 (patch)
treebbb7214c9105fbfeca4ba82056cdfc6fb107b5c2 /web-timeplot/ARCHITECTURE.md
parent2c1b37a5b0c4962b405a85768b9b8cdd5c4f1097 (diff)
pushing claude changes before i take another route
Diffstat (limited to 'web-timeplot/ARCHITECTURE.md')
-rw-r--r--web-timeplot/ARCHITECTURE.md434
1 files changed, 434 insertions, 0 deletions
diff --git a/web-timeplot/ARCHITECTURE.md b/web-timeplot/ARCHITECTURE.md
new file mode 100644
index 0000000..b49c0b0
--- /dev/null
+++ b/web-timeplot/ARCHITECTURE.md
@@ -0,0 +1,434 @@
+# TimePlot Architecture
+
+## Overview
+
+This document describes the clean, modular architecture for TimePlot's data visualization system.
+
+## Design Principles
+
+**Separation of Concerns**: Data generation, data provision, and visualization are completely separate.
+
+- **Plots don't generate data** - They only display it
+- **Data sources don't know about visualization** - They only produce data
+- **Connections manage the flow** - They link sources to plots
+
+This architecture allows you to:
+- Easily swap data sources without changing visualization
+- Reuse plots with different data
+- Test components independently
+- Support multiple data types (real-time, synthetic, replay, etc.)
+
+## Architecture Layers
+
+```
+┌─────────────────────────────────────────────────┐
+│ Application Layer (main.js) │
+│ - Manages app lifecycle │
+│ - Creates plots and sources │
+│ - Sets up connections │
+└─────────────────────────────────────────────────┘
+ │
+ ├──────────────┬──────────────┐
+ ↓ ↓ ↓
+ ┌────────────────┐ ┌─────────────┐ ┌─────────────┐
+ │ Connections │ │ Plots │ │ Sources │
+ │ (Glue Layer) │ │ (Display) │ │ (Data) │
+ └────────────────┘ └─────────────┘ └─────────────┘
+```
+
+## Core Components
+
+### 1. Data Generators (`test-data-generators.js`)
+
+**Purpose**: Generate mathematical patterns for testing
+
+**Classes**:
+- `DataGenerator` - Base class with common functionality
+- `SineWaveGenerator` - Classic sine waves
+- `SquareWaveGenerator` - Digital square waves
+- `PerlinNoiseGenerator` - Smooth noise
+- `ChirpGenerator` - Frequency sweeps
+- `CompositeGenerator` - Combine multiple generators
+- Many more...
+
+**Usage**:
+```javascript
+const generator = new SineWaveGenerator({
+ frequency: 2.0,
+ amplitude: 30,
+ sampleRate: 100,
+});
+
+// Generate a line of points
+const points = generator.generateLine(100, 800);
+```
+
+### 2. Data Sources (`data-sources.js`)
+
+**Purpose**: Provide data to plots via events
+
+**Key Concept**: Sources emit events when data is ready. They know *when* and *how* to provide data, but not *where* it goes.
+
+**Base Class**:
+```javascript
+class DataSource extends EventEmitter {
+ start() // Begin providing data
+ stop() // Stop providing data
+ emitLine() // Emit a complete line of data
+ emitPoint() // Emit a single data point
+}
+```
+
+**Events**:
+- `'line'` - Complete line of data ready: `{points, timestamp, metadata}`
+- `'point'` - Single data point ready: `{value, timestamp}`
+- `'error'` - Error occurred: `{error}`
+
+**Available Sources**:
+
+- **SyntheticDataSource** - Uses test generators, emits lines periodically
+- **FunctionDataSource** - Evaluates a function (x,t) => y
+- **StreamingDataSource** - Emits individual points at a sample rate
+- **WebSocketDataSource** - Receives real-time data from WebSocket
+- **CSVDataSource** - Replays data from CSV files
+- **CompositeDataSource** - Combines multiple sources
+
+**Example**:
+```javascript
+// Create source
+const source = new SyntheticDataSource({
+ generator: new SineWaveGenerator({ frequency: 2.0 }),
+ pointsPerLine: 100,
+ width: 800,
+ lineInterval: 100, // ms between lines
+});
+
+// Listen to events
+source.on('line', (data) => {
+ console.log('Received line:', data.points);
+});
+
+// Start generating
+source.start();
+```
+
+### 3. Plots (`timeseries-plot.js`)
+
+**Purpose**: Pure visualization - display data, nothing else
+
+**Key Concept**: Plots receive data via method calls. They know *how* to display data, but not *where* it comes from.
+
+**API**:
+```javascript
+class TimeSeriesPlot {
+ // Data input
+ addLine(points, metadata) // Add a line of data
+ addDataPoint(value, timestamp) // Add a single point
+ clearData() // Clear all data
+
+ // Display control
+ setGridVisible(visible)
+ setScrollSpeed(speed)
+ setVerticalScale(scale)
+ setTitle(title)
+
+ // Frame update
+ update() // Call every frame to scroll/render
+}
+```
+
+**Example**:
+```javascript
+// Create plot
+const plot = new TimeSeriesPlot({
+ x: 0,
+ y: 0,
+ width: 800,
+ height: 600,
+ title: 'My Plot',
+ showGrid: true,
+});
+
+// Add to PixiJS stage
+app.stage.addChild(plot.container);
+
+// Receive data
+plot.addLine([
+ {x: 0, y: 10},
+ {x: 100, y: 20},
+ {x: 200, y: 15},
+]);
+
+// Update every frame
+app.ticker.add(() => plot.update());
+```
+
+### 4. Connections (`plot-connections.js`)
+
+**Purpose**: Link data sources to plots
+
+**Key Concept**: Connections subscribe to source events and forward data to plots. They handle timing, buffering, and data transformation.
+
+**Connection Types**:
+
+**DirectConnection** - Lines go straight from source to plot
+```javascript
+const connection = new DirectConnection(source, plot);
+connection.connect();
+```
+
+**BufferedConnection** - Buffers individual points into lines
+```javascript
+const connection = new BufferedConnection(source, plot, {
+ bufferSize: 100, // Points per line
+ bufferTimeout: 1000, // Max time to wait (ms)
+});
+connection.connect();
+```
+
+**SynchronizedConnection** - Synchronizes multiple sources
+```javascript
+const connection = new SynchronizedConnection([source1, source2], plot, {
+ syncMode: 'wait-for-all',
+});
+connection.connect();
+```
+
+**Helper Functions**:
+```javascript
+// Quick setup for synthetic data
+const conn = connectSyntheticData(generator, plot, {
+ lineInterval: 100,
+});
+
+// Quick setup for function-based
+const conn = connectFunction((x, t) => Math.sin(x * 10 + t), plot);
+
+// All-in-one setup
+const {plot, source, connection} = createConnectedPlot(app, plotConfig, sourceConfig);
+```
+
+## Data Flow
+
+### Scenario 1: Synthetic Data (Test Pattern)
+
+```
+DataGenerator SyntheticDataSource DirectConnection TimeSeriesPlot
+ │ │ │ │
+ │ │ start() │ │
+ │ ├───────────────────────>│ │
+ │ │ │ │
+ │ generateLine() │ │ │
+ │<────────────────────────┤ │ │
+ │ │ │ │
+ │ returns points[] │ │ │
+ ├────────────────────────>│ │ │
+ │ │ │ │
+ │ │ emit('line', data) │ │
+ │ ├───────────────────────>│ │
+ │ │ │ │
+ │ │ │ addLine(points) │
+ │ │ ├─────────────────────>│
+ │ │ │ │
+ │ │ │ │ update()
+ │ │ │ │ (scroll & render)
+```
+
+### Scenario 2: Real-Time Streaming
+
+```
+External System WebSocketDataSource BufferedConnection TimeSeriesPlot
+ │ │ │ │
+ │ WebSocket message │ │ │
+ ├────────────────────────>│ │ │
+ │ │ │ │
+ │ │ emit('point', value) │ │
+ │ ├───────────────────────>│ │
+ │ │ │ │
+ │ │ │ buffer.push(value) │
+ │ │ │ │
+ │ │ emit('point') │ │
+ │ ├───────────────────────>│ │
+ │ │ │ │
+ │ │ (continue...) │ │
+ │ │ │ │
+ │ │ │ buffer full! │
+ │ │ │ │
+ │ │ │ flush() │
+ │ │ │ addLine(buffered) │
+ │ │ ├─────────────────────>│
+```
+
+## Usage Patterns
+
+### Pattern 1: Simple Test Visualization
+
+```javascript
+import { TimeSeriesPlot } from './timeseries-plot.js';
+import { connectSyntheticData } from './plot-connections.js';
+import { TestDataFactory } from './test-data-generators.js';
+
+// Create plot
+const plot = new TimeSeriesPlot({...});
+app.stage.addChild(plot.container);
+
+// Connect data
+const connection = connectSyntheticData(
+ TestDataFactory.createSimpleSine(30),
+ plot,
+ { lineInterval: 100 }
+);
+
+// Update loop
+app.ticker.add(() => plot.update());
+```
+
+### Pattern 2: Swap Data Sources
+
+```javascript
+// Start with one source
+let connection = connectSyntheticData(generator1, plot);
+
+// Later, switch to different data
+connection.disconnect();
+connection = connectSyntheticData(generator2, plot);
+```
+
+### Pattern 3: Multiple Plots, Different Data
+
+```javascript
+const plot1 = new TimeSeriesPlot({x: 0, y: 0, ...});
+const plot2 = new TimeSeriesPlot({x: 800, y: 0, ...});
+
+connectSyntheticData(sineGenerator, plot1);
+connectSyntheticData(noiseGenerator, plot2);
+
+app.ticker.add(() => {
+ plot1.update();
+ plot2.update();
+});
+```
+
+### Pattern 4: Real-Time WebSocket Data
+
+```javascript
+const plot = new TimeSeriesPlot({...});
+
+const source = new WebSocketDataSource({
+ url: 'ws://localhost:8080/data'
+});
+
+const connection = new BufferedConnection(source, plot, {
+ bufferSize: 100,
+});
+connection.connect();
+
+app.ticker.add(() => plot.update());
+```
+
+### Pattern 5: Custom Function
+
+```javascript
+const plot = new TimeSeriesPlot({...});
+
+const connection = connectFunction(
+ (x, t) => Math.sin(x * 10 + t * 2) + Math.cos(x * 5 - t),
+ plot,
+ { lineInterval: 100, amplitude: 30 }
+);
+
+app.ticker.add(() => plot.update());
+```
+
+## File Organization
+
+```
+web-timeplot/src/
+├── test-data-generators.js # Math generators for test patterns
+├── data-sources.js # Data provision (events)
+├── timeseries-plot.js # Pure visualization
+├── plot-connections.js # Glue layer
+├── example-usage.js # Complete examples
+├── main.js # Application entry point
+├── state.js # App state management
+└── waterfall.js # (Legacy - can be replaced)
+```
+
+## Migration Path
+
+### From Old WaterfallGraph to New Architecture
+
+**Old way** (tightly coupled):
+```javascript
+const graph = new WaterfallGraph({...});
+// Graph generates its own data
+```
+
+**New way** (separated):
+```javascript
+const plot = new TimeSeriesPlot({...});
+const source = new SyntheticDataSource({...});
+const connection = new DirectConnection(source, plot);
+connection.connect();
+```
+
+## Extension Points
+
+### Adding New Data Sources
+
+Extend `DataSource` and implement:
+- `start()` - Begin providing data
+- `stop()` - Stop providing data
+- Call `emitLine()` or `emitPoint()` when data is ready
+
+Example:
+```javascript
+class MyCustomSource extends DataSource {
+ start() {
+ super.start();
+ // Start your data provision mechanism
+ this.interval = setInterval(() => {
+ const points = [...]; // Generate points
+ this.emitLine(points);
+ }, 100);
+ }
+
+ stop() {
+ super.stop();
+ clearInterval(this.interval);
+ }
+}
+```
+
+### Adding New Connection Types
+
+Extend `PlotConnection` and implement `setupSubscriptions()`:
+
+```javascript
+class MyCustomConnection extends PlotConnection {
+ setupSubscriptions() {
+ const unsub = this.source.on('line', (data) => {
+ // Transform data as needed
+ const transformed = this.transformData(data);
+ this.plot.addLine(transformed.points);
+ });
+ this.subscriptions.push(unsub);
+ }
+}
+```
+
+## Benefits of This Architecture
+
+1. **Testability** - Each component can be tested independently
+2. **Reusability** - Plots work with any data source
+3. **Flexibility** - Easy to add new data sources or visualizations
+4. **Maintainability** - Clear responsibilities, easy to understand
+5. **Performance** - Can optimize each layer independently
+6. **Real-world ready** - Supports actual data sources (WebSocket, files, etc.)
+
+## Next Steps
+
+- Replace old `waterfall.js` usage with new `TimeSeriesPlot`
+- Create real data sources for your application
+- Add more visualization types (heatmap, spectrogram, etc.)
+- Add data processing layer (filtering, FFT, etc.)