diff options
Diffstat (limited to 'web-timeplot/src/main.js')
| -rw-r--r-- | web-timeplot/src/main.js | 220 |
1 files changed, 220 insertions, 0 deletions
diff --git a/web-timeplot/src/main.js b/web-timeplot/src/main.js new file mode 100644 index 0000000..0d065f7 --- /dev/null +++ b/web-timeplot/src/main.js @@ -0,0 +1,220 @@ +import { Application } from 'pixi.js'; +import { WaterfallGraph } from './waterfall.js'; +import { PerformanceMetrics } from './metrics.js'; + +class TimePlot { + constructor() { + this.app = null; + this.graphs = []; + this.metrics = new PerformanceMetrics(60, 10000); + this.showMetrics = true; + this.showGrid = true; + this.time = 0; + } + + async init() { + const container = document.getElementById('canvas-container'); + + // Try WebGPU first, fallback to WebGL + let preferredRenderer = 'webgpu'; + let preference = 'webgpu'; + + // Check WebGPU availability + if (!navigator.gpu) { + console.log('WebGPU not available, using WebGL'); + preferredRenderer = 'webgl'; + preference = 'webgl'; + } + + try { + this.app = new Application(); + + await this.app.init({ + preference: preference, + width: window.innerWidth, + height: window.innerHeight - 60, // Account for controls + backgroundColor: 0x1a1a26, + antialias: true, + autoDensity: true, + resolution: window.devicePixelRatio || 1, + }); + + container.appendChild(this.app.canvas); + + // Display actual renderer type + const rendererType = this.app.renderer.type; + document.getElementById('renderer-type').textContent = rendererType; + console.log('Using renderer:', rendererType); + + // Create two waterfall graphs side by side + this.setupGraphs(); + this.setupControls(); + this.setupKeyboard(); + this.handleResize(); + + // Start animation loop + this.app.ticker.add(() => this.update()); + + console.log('TimePlot initialized successfully'); + + } catch (error) { + console.error('Failed to initialize PixiJS:', error); + + // Ultimate fallback + if (preference === 'webgpu') { + console.log('Retrying with WebGL...'); + this.init(); // Retry will use WebGL + } + } + } + + setupGraphs() { + const width = this.app.screen.width; + const height = this.app.screen.height; + + // Left graph + const graph1 = new WaterfallGraph({ + x: 0, + y: 0, + width: width / 2, + height: height, + title: 'Frequency vs Time', + color: 0xff6666, + }); + + // Right graph + const graph2 = new WaterfallGraph({ + x: width / 2, + y: 0, + width: width / 2, + height: height, + title: 'Position vs Time', + color: 0x66ff66, + }); + + this.graphs.push(graph1, graph2); + this.graphs.forEach(graph => { + this.app.stage.addChild(graph.container); + }); + } + + setupControls() { + document.getElementById('toggle-grid').addEventListener('click', () => { + this.toggleGrid(); + }); + + document.getElementById('toggle-metrics').addEventListener('click', () => { + this.toggleMetrics(); + }); + + document.getElementById('export-metrics').addEventListener('click', () => { + this.exportMetrics(); + }); + + this.updateControlButtons(); + } + + setupKeyboard() { + window.addEventListener('keydown', (e) => { + switch(e.key.toLowerCase()) { + case 'g': + this.toggleGrid(); + break; + case 'm': + this.toggleMetrics(); + break; + case 'e': + this.exportMetrics(); + break; + } + }); + } + + handleResize() { + window.addEventListener('resize', () => { + const width = window.innerWidth; + const height = window.innerHeight - 60; + + this.app.renderer.resize(width, height); + + // Update graphs + this.graphs[0]?.resize(0, 0, width / 2, height); + this.graphs[1]?.resize(width / 2, 0, width / 2, height); + }); + } + + update() { + this.metrics.beginFrame(); + this.metrics.beginUpdate(); + + this.time += 0.016; // ~60fps increment + + // Update each graph + this.graphs.forEach((graph, idx) => { + graph.update(this.time, idx); + }); + + const updateMs = this.metrics.endUpdate(); + + this.metrics.beginRender(); + + // Rendering happens automatically via PixiJS + + const renderMs = this.metrics.endRender(); + + // Calculate total vertices (estimate) + const vertexCount = this.graphs.reduce((sum, g) => sum + g.getVertexCount(), 0); + const lineCount = this.graphs.reduce((sum, g) => sum + g.getLineCount(), 0); + + this.metrics.endFrame(updateMs, renderMs, vertexCount, lineCount); + + // Update UI + if (this.showMetrics) { + this.updateMetricsDisplay(); + } + } + + toggleGrid() { + this.showGrid = !this.showGrid; + this.graphs.forEach(graph => graph.setGridVisible(this.showGrid)); + this.updateControlButtons(); + console.log('Grid:', this.showGrid ? 'ON' : 'OFF'); + } + + toggleMetrics() { + this.showMetrics = !this.showMetrics; + this.updateControlButtons(); + console.log('Metrics:', this.showMetrics ? 'ON' : 'OFF'); + } + + exportMetrics() { + const csv = this.metrics.exportToCSV(); + const blob = new Blob([csv], { type: 'text/csv' }); + const url = URL.createObjectURL(blob); + const a = document.createElement('a'); + a.href = url; + a.download = `timeplot-metrics-${Date.now()}.csv`; + a.click(); + URL.revokeObjectURL(url); + console.log('Metrics exported'); + } + + updateMetricsDisplay() { + const display = document.getElementById('metrics-display'); + if (display) { + display.textContent = this.metrics.formatSummary(); + } + } + + updateControlButtons() { + const gridBtn = document.getElementById('toggle-grid'); + const metricsBtn = document.getElementById('toggle-metrics'); + + gridBtn.classList.toggle('active', this.showGrid); + metricsBtn.classList.toggle('active', this.showMetrics); + } +} + +// Initialize the application +const app = new TimePlot(); +app.init().catch(console.error); |
