diff options
Diffstat (limited to 'src/example-usage.js')
| -rw-r--r-- | src/example-usage.js | 535 |
1 files changed, 535 insertions, 0 deletions
diff --git a/src/example-usage.js b/src/example-usage.js new file mode 100644 index 0000000..67eff4b --- /dev/null +++ b/src/example-usage.js @@ -0,0 +1,535 @@ +/** + * Example Usage: Complete examples of the new architecture + * + * This file demonstrates how to use the separated data/visualization architecture: + * - TimeSeriesPlot: Pure visualization + * - DataSource: Data generation/provision + * - Connections: Links between them + */ + +import { Application } from 'pixi.js'; +import { TimeSeriesPlot } from './timeseries-plot.js'; +import { + SyntheticDataSource, + FunctionDataSource, + StreamingDataSource, + WebSocketDataSource, +} from './data-sources.js'; +import { + DirectConnection, + BufferedConnection, + ConnectionManager, + connectSyntheticData, + connectFunction, + createConnectedPlot, +} from './plot-connections.js'; +import { + TestDataFactory, + SineWaveGenerator, + PerlinNoiseGenerator, + ChirpGenerator, +} from './test-data-generators.js'; + +// ============================================================================ +// Example 1: Simple Setup - One plot, one data source +// ============================================================================ + +export async function example1_SimpleSetup() { + console.log('=== Example 1: Simple Setup ==='); + + // Create PixiJS app + const app = new Application(); + await app.init({ + width: 800, + height: 600, + backgroundColor: 0x1a1a26, + }); + document.body.appendChild(app.canvas); + + // Create plot (visualization only) + const plot = new TimeSeriesPlot({ + x: 0, + y: 0, + width: 800, + height: 600, + title: 'Simple Sine Wave', + showGrid: true, + }); + app.stage.addChild(plot.container); + + // Create data source + const generator = TestDataFactory.createSimpleSine(30); + const source = new SyntheticDataSource({ + generator: generator, + pointsPerLine: 100, + width: 800, + lineInterval: 100, // New line every 100ms + }); + + // Connect source to plot + const connection = new DirectConnection(source, plot); + connection.connect(); + + // Update plot every frame + app.ticker.add(() => { + plot.update(); + }); + + return { app, plot, source, connection }; +} + +// ============================================================================ +// Example 2: Quick Setup Using Helper Functions +// ============================================================================ + +export async function example2_QuickSetup() { + console.log('=== Example 2: Quick Setup ==='); + + const app = new Application(); + await app.init({ + width: 800, + height: 600, + backgroundColor: 0x1a1a26, + }); + document.body.appendChild(app.canvas); + + // One-liner setup! + const { plot, source, connection } = createConnectedPlot( + app, + { + x: 0, + y: 0, + width: 800, + height: 600, + title: 'Quick Setup', + }, + { + generator: TestDataFactory.createComplexPattern(30), + lineInterval: 100, + } + ); + + app.ticker.add(() => plot.update()); + + return { app, plot, source, connection }; +} + +// ============================================================================ +// Example 3: Multiple Plots with Different Data Sources +// ============================================================================ + +export async function example3_MultiplePlots() { + console.log('=== Example 3: Multiple Plots ==='); + + const app = new Application(); + await app.init({ + width: 1600, + height: 600, + backgroundColor: 0x1a1a26, + }); + document.body.appendChild(app.canvas); + + const width = 800; + const height = 600; + + // Left plot: Sine wave + const plot1 = new TimeSeriesPlot({ + x: 0, + y: 0, + width: width, + height: height, + title: 'Sine Wave', + color: 0xff6666, + }); + + // Right plot: Perlin noise + const plot2 = new TimeSeriesPlot({ + x: width, + y: 0, + width: width, + height: height, + title: 'Perlin Noise', + color: 0x66ff66, + }); + + app.stage.addChild(plot1.container); + app.stage.addChild(plot2.container); + + // Connect different data sources + const conn1 = connectSyntheticData( + TestDataFactory.createSimpleSine(30), + plot1, + { lineInterval: 100 } + ); + + const conn2 = connectSyntheticData( + TestDataFactory.createSmoothNoise(30), + plot2, + { lineInterval: 100 } + ); + + app.ticker.add(() => { + plot1.update(); + plot2.update(); + }); + + return { app, plots: [plot1, plot2], connections: [conn1, conn2] }; +} + +// ============================================================================ +// Example 4: Using Function-Based Data Source +// ============================================================================ + +export async function example4_FunctionSource() { + console.log('=== Example 4: Function Source ==='); + + const app = new Application(); + await app.init({ width: 800, height: 600, backgroundColor: 0x1a1a26 }); + document.body.appendChild(app.canvas); + + const plot = new TimeSeriesPlot({ + x: 0, + y: 0, + width: 800, + height: 600, + title: 'Custom Function', + }); + app.stage.addChild(plot.container); + + // Define a custom function: (x, t) => y + // x is normalized 0-1 across the width + // t is time in seconds + const customFunc = (x, t) => { + // Create an interference pattern + const wave1 = Math.sin(x * 10 + t * 2); + const wave2 = Math.sin(x * 15 - t * 3); + const wave3 = Math.cos(x * 8 + t * 1.5); + return (wave1 + wave2 + wave3) / 3; + }; + + const connection = connectFunction(customFunc, plot, { + lineInterval: 100, + amplitude: 30, + }); + + app.ticker.add(() => plot.update()); + + return { app, plot, connection }; +} + +// ============================================================================ +// Example 5: Swapping Data Sources at Runtime +// ============================================================================ + +export async function example5_SwappingSources() { + console.log('=== Example 5: Swapping Sources ==='); + + const app = new Application(); + await app.init({ width: 800, height: 600, backgroundColor: 0x1a1a26 }); + document.body.appendChild(app.canvas); + + const plot = new TimeSeriesPlot({ + x: 0, + y: 0, + width: 800, + height: 600, + title: 'Dynamic Source Switching', + }); + app.stage.addChild(plot.container); + + // Start with sine wave + let currentConnection = connectSyntheticData( + TestDataFactory.createSimpleSine(30), + plot, + { lineInterval: 100 } + ); + + app.ticker.add(() => plot.update()); + + // Function to switch to a different data source + const switchToSource = (generator, title) => { + // Disconnect current source + currentConnection.disconnect(); + + // Connect new source + currentConnection = connectSyntheticData(generator, plot, { + lineInterval: 100, + }); + + plot.setTitle(title); + console.log(`Switched to: ${title}`); + }; + + // Example: Switch sources every 5 seconds + let sourceIndex = 0; + const sources = [ + { gen: TestDataFactory.createSimpleSine(30), title: 'Sine Wave' }, + { gen: TestDataFactory.createComplexPattern(30), title: 'Complex Pattern' }, + { gen: TestDataFactory.createSmoothNoise(30), title: 'Perlin Noise' }, + { gen: TestDataFactory.createFrequencySweep(30), title: 'Frequency Sweep' }, + ]; + + setInterval(() => { + sourceIndex = (sourceIndex + 1) % sources.length; + const source = sources[sourceIndex]; + switchToSource(source.gen, source.title); + }, 5000); + + return { app, plot, switchToSource }; +} + +// ============================================================================ +// Example 6: Streaming Data with Buffering +// ============================================================================ + +export async function example6_StreamingData() { + console.log('=== Example 6: Streaming Data ==='); + + const app = new Application(); + await app.init({ width: 800, height: 600, backgroundColor: 0x1a1a26 }); + document.body.appendChild(app.canvas); + + const plot = new TimeSeriesPlot({ + x: 0, + y: 0, + width: 800, + height: 600, + title: 'Streaming Data (Buffered)', + }); + app.stage.addChild(plot.container); + + // Create streaming source (emits individual points) + const generator = new SineWaveGenerator({ + frequency: 2.0, + amplitude: 1.0, + sampleRate: 60, + }); + + const source = new StreamingDataSource({ + generator: generator, + sampleRate: 60, // 60 points per second + }); + + // Use buffered connection to assemble points into lines + const connection = new BufferedConnection(source, plot, { + bufferSize: 100, // Buffer 100 points before creating a line + bufferTimeout: 1000, // Or timeout after 1 second + }); + connection.connect(); + + app.ticker.add(() => plot.update()); + + return { app, plot, source, connection }; +} + +// ============================================================================ +// Example 7: Connection Manager (Managing Multiple Connections) +// ============================================================================ + +export async function example7_ConnectionManager() { + console.log('=== Example 7: Connection Manager ==='); + + const app = new Application(); + await app.init({ width: 800, height: 600, backgroundColor: 0x1a1a26 }); + document.body.appendChild(app.canvas); + + const plot = new TimeSeriesPlot({ + x: 0, + y: 0, + width: 800, + height: 600, + title: 'Managed Connections', + }); + app.stage.addChild(plot.container); + + // Create connection manager + const manager = new ConnectionManager(); + + // Add first connection + const source1 = new SyntheticDataSource({ + generator: TestDataFactory.createSimpleSine(30), + pointsPerLine: 100, + width: 800, + lineInterval: 100, + }); + + const connId1 = manager.connect(source1, plot, { type: 'direct' }); + console.log('Connection ID:', connId1); + + app.ticker.add(() => plot.update()); + + // Later: disconnect and switch to different source + setTimeout(() => { + manager.disconnect(connId1); + + const source2 = new SyntheticDataSource({ + generator: TestDataFactory.createFrequencySweep(30), + pointsPerLine: 100, + width: 800, + lineInterval: 100, + }); + + const connId2 = manager.connect(source2, plot, { type: 'direct' }); + plot.setTitle('Frequency Sweep'); + console.log('Switched to connection:', connId2); + }, 5000); + + return { app, plot, manager }; +} + +// ============================================================================ +// Example 8: Complete Interactive Demo +// ============================================================================ + +export async function example8_InteractiveDemo() { + console.log('=== Example 8: Interactive Demo ==='); + + const app = new Application(); + await app.init({ + width: 1600, + height: 800, + backgroundColor: 0x1a1a26, + }); + document.body.appendChild(app.canvas); + + // Create two plots + const plot1 = new TimeSeriesPlot({ + x: 0, + y: 0, + width: 800, + height: 800, + title: 'Plot 1 - Press 1-5 to change', + color: 0xff6666, + }); + + const plot2 = new TimeSeriesPlot({ + x: 800, + y: 0, + width: 800, + height: 800, + title: 'Plot 2 - Press 6-0 to change', + color: 0x66ff66, + }); + + app.stage.addChild(plot1.container); + app.stage.addChild(plot2.container); + + // Connection manager + const manager = new ConnectionManager(); + + // Available data sources + const dataSources = { + sine: () => TestDataFactory.createSimpleSine(30), + complex: () => TestDataFactory.createComplexPattern(30), + noise: () => TestDataFactory.createSmoothNoise(30), + sweep: () => TestDataFactory.createFrequencySweep(30), + burst: () => TestDataFactory.createBurstySignal(30), + }; + + // Track current connections + let conn1Id = null; + let conn2Id = null; + + // Helper to switch source + const switchSource = (plot, generatorFunc, title) => { + // Disconnect old connection + const connId = plot === plot1 ? conn1Id : conn2Id; + if (connId !== null) { + manager.disconnect(connId); + } + + // Create new connection + const source = new SyntheticDataSource({ + generator: generatorFunc(), + pointsPerLine: 100, + width: plot.width, + lineInterval: 100, + }); + + const newConnId = manager.connect(source, plot, { type: 'direct' }); + plot.setTitle(title); + + // Store connection ID + if (plot === plot1) { + conn1Id = newConnId; + } else { + conn2Id = newConnId; + } + }; + + // Initialize with default sources + switchSource(plot1, dataSources.sine, 'Plot 1 - Sine Wave'); + switchSource(plot2, dataSources.complex, 'Plot 2 - Complex Pattern'); + + // Keyboard controls + window.addEventListener('keydown', (e) => { + switch (e.key) { + case '1': + switchSource(plot1, dataSources.sine, 'Plot 1 - Sine Wave'); + break; + case '2': + switchSource(plot1, dataSources.complex, 'Plot 1 - Complex Pattern'); + break; + case '3': + switchSource(plot1, dataSources.noise, 'Plot 1 - Perlin Noise'); + break; + case '4': + switchSource(plot1, dataSources.sweep, 'Plot 1 - Frequency Sweep'); + break; + case '5': + switchSource(plot1, dataSources.burst, 'Plot 1 - Burst Signal'); + break; + case '6': + switchSource(plot2, dataSources.sine, 'Plot 2 - Sine Wave'); + break; + case '7': + switchSource(plot2, dataSources.complex, 'Plot 2 - Complex Pattern'); + break; + case '8': + switchSource(plot2, dataSources.noise, 'Plot 2 - Perlin Noise'); + break; + case '9': + switchSource(plot2, dataSources.sweep, 'Plot 2 - Frequency Sweep'); + break; + case '0': + switchSource(plot2, dataSources.burst, 'Plot 2 - Burst Signal'); + break; + case 'g': + plot1.setGridVisible(!plot1.showGrid); + plot2.setGridVisible(!plot2.showGrid); + break; + case 'c': + plot1.clearData(); + plot2.clearData(); + break; + } + }); + + // Update loop + app.ticker.add(() => { + plot1.update(); + plot2.update(); + }); + + console.log('Controls:'); + console.log(' 1-5: Change Plot 1 source'); + console.log(' 6-0: Change Plot 2 source'); + console.log(' G: Toggle grid'); + console.log(' C: Clear data'); + + return { app, plot1, plot2, manager }; +} + +// ============================================================================ +// Quick Test: Run one of the examples +// ============================================================================ + +// Uncomment to run an example: +// example1_SimpleSetup(); +// example2_QuickSetup(); +// example3_MultiplePlots(); +// example4_FunctionSource(); +// example5_SwappingSources(); +// example6_StreamingData(); +// example7_ConnectionManager(); +//example8_InteractiveDemo(); |
