diff options
Diffstat (limited to 'web-timeplot')
| -rw-r--r-- | web-timeplot/src/main.js | 427 | ||||
| -rw-r--r-- | web-timeplot/src/template-for-standard-site.js | 75 |
2 files changed, 324 insertions, 178 deletions
diff --git a/web-timeplot/src/main.js b/web-timeplot/src/main.js index 0d065f7..abcd72f 100644 --- a/web-timeplot/src/main.js +++ b/web-timeplot/src/main.js @@ -2,219 +2,290 @@ 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'; - } +// ============================================================================ +// GLOBAL STATE +// ============================================================================ + +let env = {}; +let cfg = { + showGrid: true, + showMetrics: true, + rollingWindow: 60, + historyCapacity: 10000, +}; + +let dom = { + container: null, + controls: {}, + display: {}, +}; + +let app = null; // PixiJS Application +let graphs = []; +let metrics = null; +let time = 0; + +// ============================================================================ +// APPLICATION ENTRY POINT +// ============================================================================ + +document.addEventListener('DOMContentLoaded', async function() { + log('TimePlot starting...'); + + log('init DOM'); + await initDOM(); + + log('init config'); + await initCfg(); + + log('init PixiJS renderer'); + await initRenderer(); + + log('init graphs'); + await initGraphs(); + + log('init services'); + await initServices(); + + log('setup controls'); + await setupControls(); + + log('TimePlot ready'); +}); + +// ============================================================================ +// INITIALIZATION FUNCTIONS +// ============================================================================ + +async function initDOM() { + dom.container = document.getElementById('canvas-container'); + dom.controls.gridBtn = document.getElementById('toggle-grid'); + dom.controls.metricsBtn = document.getElementById('toggle-metrics'); + dom.controls.exportBtn = document.getElementById('export-metrics'); + dom.display.rendererType = document.getElementById('renderer-type'); + dom.display.metrics = document.getElementById('metrics-display'); +} +async function initCfg() { + const localCfg = localStorage.getItem('timeplot-cfg'); + if (localCfg) { 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 - } + const parsed = JSON.parse(localCfg); + cfg = { ...cfg, ...parsed }; + log('Loaded config from localStorage'); + } catch (e) { + log('Failed to parse config, using defaults'); } } +} - 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, - }); +function saveCfg() { + localStorage.setItem('timeplot-cfg', JSON.stringify(cfg)); +} - // Right graph - const graph2 = new WaterfallGraph({ - x: width / 2, - y: 0, - width: width / 2, - height: height, - title: 'Position vs Time', - color: 0x66ff66, +async function initRenderer() { + // Check WebGPU availability + let preference = 'webgpu'; + if (!navigator.gpu) { + log('WebGPU not available, using WebGL'); + preference = 'webgl'; + } + console.log(navigator.gpu); + console.log(navigator); + + try { + app = new Application(); + + await app.init({ + preference: preference, + width: window.innerWidth, + height: window.innerHeight - 60, // Account for controls + backgroundColor: 0x1a1a26, + antialias: true, + autoDensity: true, + resolution: window.devicePixelRatio || 1, }); - this.graphs.push(graph1, graph2); - this.graphs.forEach(graph => { - this.app.stage.addChild(graph.container); - }); + dom.container.appendChild(app.canvas); + + // Display actual renderer type + const rendererType = app.renderer.type; + dom.display.rendererType.textContent = rendererType; + log(`Using renderer: ${rendererType}`); + + // Handle window resize + window.addEventListener('resize', handleResize); + + } catch (error) { + log(`Failed to initialize renderer: ${error}`); + throw error; } +} - setupControls() { - document.getElementById('toggle-grid').addEventListener('click', () => { - this.toggleGrid(); - }); +async function initGraphs() { + const width = app.screen.width; + const height = 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, + }); + + graphs.push(graph1, graph2); + graphs.forEach(graph => { + app.stage.addChild(graph.container); + graph.setGridVisible(cfg.showGrid); + }); +} - document.getElementById('toggle-metrics').addEventListener('click', () => { - this.toggleMetrics(); - }); +async function initServices() { + // Initialize performance metrics + metrics = new PerformanceMetrics(cfg.rollingWindow, cfg.historyCapacity); - document.getElementById('export-metrics').addEventListener('click', () => { - this.exportMetrics(); - }); + // Start animation loop + app.ticker.add(update); - this.updateControlButtons(); - } + log('Services initialized'); +} - setupKeyboard() { - window.addEventListener('keydown', (e) => { - switch(e.key.toLowerCase()) { - case 'g': - this.toggleGrid(); - break; - case 'm': - this.toggleMetrics(); - break; - case 'e': - this.exportMetrics(); - break; - } - }); - } +function setupControls() { + // Button controls + dom.controls.gridBtn.addEventListener('click', toggleGrid); + dom.controls.metricsBtn.addEventListener('click', toggleMetrics); + dom.controls.exportBtn.addEventListener('click', exportMetrics); - handleResize() { - window.addEventListener('resize', () => { - const width = window.innerWidth; - const height = window.innerHeight - 60; + // Keyboard controls + window.addEventListener('keydown', handleKeyboard); - this.app.renderer.resize(width, height); + // Update button states + updateControlButtons(); +} - // Update graphs - this.graphs[0]?.resize(0, 0, width / 2, height); - this.graphs[1]?.resize(width / 2, 0, width / 2, height); - }); +// ============================================================================ +// EVENT HANDLERS +// ============================================================================ + +function handleKeyboard(e) { + switch(e.key.toLowerCase()) { + case 'g': + toggleGrid(); + break; + case 'm': + toggleMetrics(); + break; + case 'e': + exportMetrics(); + break; } +} - update() { - this.metrics.beginFrame(); - this.metrics.beginUpdate(); +function handleResize() { + const width = window.innerWidth; + const height = window.innerHeight - 60; - this.time += 0.016; // ~60fps increment + app.renderer.resize(width, height); - // Update each graph - this.graphs.forEach((graph, idx) => { - graph.update(this.time, idx); - }); + // Update graphs + if (graphs[0]) graphs[0].resize(0, 0, width / 2, height); + if (graphs[1]) graphs[1].resize(width / 2, 0, width / 2, height); +} - const updateMs = this.metrics.endUpdate(); +// ============================================================================ +// MAIN UPDATE LOOP +// ============================================================================ - this.metrics.beginRender(); +function update() { + metrics.beginFrame(); + metrics.beginUpdate(); - // Rendering happens automatically via PixiJS + time += 0.016; // ~60fps increment - const renderMs = this.metrics.endRender(); + // Update each graph + graphs.forEach((graph, idx) => { + graph.update(time, idx); + }); - // 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); + const updateMs = metrics.endUpdate(); - this.metrics.endFrame(updateMs, renderMs, vertexCount, lineCount); + metrics.beginRender(); + // Rendering happens automatically via PixiJS + const renderMs = metrics.endRender(); - // Update UI - if (this.showMetrics) { - this.updateMetricsDisplay(); - } - } + // Calculate stats + const vertexCount = graphs.reduce((sum, g) => sum + g.getVertexCount(), 0); + const lineCount = graphs.reduce((sum, g) => sum + g.getLineCount(), 0); - toggleGrid() { - this.showGrid = !this.showGrid; - this.graphs.forEach(graph => graph.setGridVisible(this.showGrid)); - this.updateControlButtons(); - console.log('Grid:', this.showGrid ? 'ON' : 'OFF'); - } + metrics.endFrame(updateMs, renderMs, vertexCount, lineCount); - toggleMetrics() { - this.showMetrics = !this.showMetrics; - this.updateControlButtons(); - console.log('Metrics:', this.showMetrics ? 'ON' : 'OFF'); + // Update UI + if (cfg.showMetrics) { + updateMetricsDisplay(); } - 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'); - } + console.log(metrics); +} - updateMetricsDisplay() { - const display = document.getElementById('metrics-display'); - if (display) { - display.textContent = this.metrics.formatSummary(); - } - } +// ============================================================================ +// UI CONTROL FUNCTIONS +// ============================================================================ + +function toggleGrid() { + cfg.showGrid = !cfg.showGrid; + graphs.forEach(graph => graph.setGridVisible(cfg.showGrid)); + updateControlButtons(); + saveCfg(); + log(`Grid: ${cfg.showGrid ? 'ON' : 'OFF'}`); +} - updateControlButtons() { - const gridBtn = document.getElementById('toggle-grid'); - const metricsBtn = document.getElementById('toggle-metrics'); +function toggleMetrics() { + cfg.showMetrics = !cfg.showMetrics; + updateControlButtons(); + saveCfg(); + log(`Metrics: ${cfg.showMetrics ? 'ON' : 'OFF'}`); +} - gridBtn.classList.toggle('active', this.showGrid); - metricsBtn.classList.toggle('active', this.showMetrics); +function exportMetrics() { + const csv = 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); + log('Metrics exported'); +} + +function updateMetricsDisplay() { + if (dom.display.metrics) { + dom.display.metrics.textContent = metrics.formatSummary(); } } -// Initialize the application -const app = new TimePlot(); -app.init().catch(console.error); +function updateControlButtons() { + dom.controls.gridBtn.classList.toggle('active', cfg.showGrid); + dom.controls.metricsBtn.classList.toggle('active', cfg.showMetrics); +} + +// ============================================================================ +// UTILITIES +// ============================================================================ + +function log(msg) { + console.log(`[TimePlot] ${msg}`); +} diff --git a/web-timeplot/src/template-for-standard-site.js b/web-timeplot/src/template-for-standard-site.js new file mode 100644 index 0000000..54aacc7 --- /dev/null +++ b/web-timeplot/src/template-for-standard-site.js @@ -0,0 +1,75 @@ +//import { setupRenderSystem } from './render.js'; + +let ENVURL = "" //remote server from which to grab env +let env = {}; +let cfg = {}; //the user config +let dom = { + input: {}, + label: {}, + box: {}, //an info-containing box + icon: {}, + info: {} +}; + + +//APP START HERE +$(document).ready(async function() { + console.log('asdf'); + //the core loop of the client application + // 1. setup relationship with DOM and grab references to its elements + log('init DOM'); + await initDOM(); + + log('init cfg'); + await initCfg(); + + log('get env vars'); + await getServerEnvVars(); + + log('init services'); + await initServices(); + + //setupRenderSystem(); + + +}); + +//gets user config from local storage if there is any +function initCfg(){ + let localCfg = localStorage.getItem('cfg'); + if (localCfg) { + try { + cfg = JSON.parse(localCfg); + } catch (e) { + cfg = {}; + } + } else { + + } +} + +async function getServerEnvVars(){ + await axios.get(`${ENVURL}`).then((res)=>{ + env = res.data; + //log(env); + }).catch((err)=>{ + //log(err); + }); + log('') +} + +function initServices(){ + //connect to websocket server + //grab endpoints from cfg +} + +function initDOM(){ + dom.body = $('body')[0]; +} + +function log(msg, lvl=1){ + if (dom.debugInfo){ + dom.debugInfo.innerHTML = msg; //TODO running log + timestamp + } + console.log(msg); +}
\ No newline at end of file |
