diff --git a/text_graph.html b/text_graph.html index bdf39f5..75badd5 100644 --- a/text_graph.html +++ b/text_graph.html @@ -322,6 +322,12 @@ const binDuration = parseFloat(document.getElementById('bin-duration').value) * 1000; // Convert to milliseconds const xAxisLabelFormat = document.getElementById('x-axis-format').value; + // Stop any existing bin rotation timer before recreating chart + if (chart && chart.binCheckInterval) { + clearInterval(chart.binCheckInterval); + chart.binCheckInterval = null; + } + // Recreate chart with new settings chart = new ASCIIBarChart('chart-container', { maxHeight: chartHeight, diff --git a/text_graph.js b/text_graph.js index a0d4315..ea36504 100644 --- a/text_graph.js +++ b/text_graph.js @@ -112,7 +112,8 @@ class ASCIIBarChart { const yAxisPadding = this.yAxisLabel ? 2 : 0; const yAxisNumbers = 3; // Width of Y-axis numbers const separator = 1; // The '|' character - const dataWidth = dataLength * 2; // Each column is 2 characters wide + // const dataWidth = dataLength * 2; // Each column is 2 characters wide // TEMP: commented for no-space test + const dataWidth = dataLength; // Each column is 1 character wide // TEMP: adjusted for no-space columns const padding = 1; // Extra padding const totalWidth = yAxisPadding + yAxisNumbers + separator + dataWidth + padding; @@ -140,8 +141,9 @@ class ASCIIBarChart { // Calculate optimal font size // For monospace fonts, character width is approximately 0.6 * font size - const charWidthRatio = 0.6; - const padding = 40; // Account for container padding + // Use a slightly smaller ratio to fit more content + const charWidthRatio = 0.7; + const padding = 30; // Reduce padding to fit more content const availableWidth = containerWidth - padding; const optimalFontSize = Math.floor((availableWidth / chartWidth) / charWidthRatio); @@ -174,11 +176,21 @@ class ASCIIBarChart { this.container.textContent = 'No data yet. Click Start to begin.'; return; } - // Only render the most recent bins up to maxDataPoints - // Reverse the order so the most recent (active) bin is on the left + // Always create a fixed-length array filled with 0s, then overlay actual bin data + dataToRender = new Array(this.maxDataPoints).fill(0); + + // Overlay actual bin data (most recent bins, reversed for left-to-right display) const startIndex = Math.max(0, this.bins.length - this.maxDataPoints); const recentBins = this.bins.slice(startIndex); - dataToRender = recentBins.reverse().map(bin => bin.count); // Reverse so active bin is first (leftmost) + + // Reverse the bins so most recent is on the left, and overlay onto the fixed array + recentBins.reverse().forEach((bin, index) => { + if (index < this.maxDataPoints) { + dataToRender[index] = bin.count; + } + }); + + console.log('render() dataToRender:', dataToRender, 'bins length:', this.bins.length); maxValue = Math.max(...dataToRender); minValue = Math.min(...dataToRender); valueRange = maxValue - minValue; @@ -208,7 +220,8 @@ class ASCIIBarChart { // Add title if provided (centered) if (this.title) { - const chartWidth = 4 + this.maxDataPoints * 2; // Y-axis numbers + data columns + // const chartWidth = 4 + this.maxDataPoints * 2; // Y-axis numbers + data columns // TEMP: commented for no-space test + const chartWidth = 4 + this.maxDataPoints; // Y-axis numbers + data columns // TEMP: adjusted for no-space columns const titlePadding = Math.floor((chartWidth - this.title.length) / 2); output += yAxisPadding + ' '.repeat(Math.max(0, titlePadding)) + this.title + '\n\n'; } @@ -242,9 +255,11 @@ class ASCIIBarChart { const scaledHeight = Math.ceil(count / scaleFactor); if (scaledHeight >= row) { - line += ' X'; + // line += ' X'; // TEMP: commented out space between columns + line += 'X'; // TEMP: no space between columns } else { - line += ' '; + // line += ' '; // TEMP: commented out space between columns + line += ' '; // TEMP: single space for empty columns } } @@ -252,33 +267,57 @@ class ASCIIBarChart { } // Draw X-axis - output += yAxisPadding + ' +' + '-'.repeat(this.maxDataPoints * 2) + '\n'; + // output += yAxisPadding + ' +' + '-'.repeat(this.maxDataPoints * 2) + '\n'; // TEMP: commented out for no-space test + output += yAxisPadding + ' +' + '-'.repeat(this.maxDataPoints) + '\n'; // TEMP: back to original length // Draw X-axis labels based on mode and format - let xAxisLabels = yAxisPadding + ' '; + let xAxisLabels = yAxisPadding + ' '; // Initial padding to align with X-axis + let labels = []; for (let i = 0; i < this.maxDataPoints; i++) { if (i % 5 === 0) { let label = ''; if (this.useBinMode) { // For bin mode, show labels for all possible positions // i=0 is leftmost (most recent), i=maxDataPoints-1 is rightmost (oldest) - const elapsedSec = i * Math.floor(this.binDuration / 1000); - label = String(elapsedSec).padStart(2, ' ') + 's'; + const elapsedSec = (i * this.binDuration) / 1000; + // Format with appropriate precision for sub-second bins + if (this.binDuration < 1000) { + // Show decimal seconds for sub-second bins + label = elapsedSec.toFixed(1) + 's'; + } else { + // Show whole seconds for 1+ second bins + label = ' ' + String(Math.round(elapsedSec)) + 's'; + } } else { // For legacy mode, show data point numbers const startIndex = Math.max(1, this.totalDataPoints - this.maxDataPoints + 1); - label = String(startIndex + i).padStart(2, ' '); + label = String(startIndex + i).padStart(3, ' '); } - xAxisLabels += label; - } else { - xAxisLabels += ' '; + labels.push({ position: i, text: label }); } } + + // Position all labels + let currentPosition = yAxisPadding.length + 3; // Start after initial padding + for (const labelInfo of labels) { + const spacesBefore = Math.max(0, labelInfo.position - currentPosition); + xAxisLabels += ' '.repeat(spacesBefore) + labelInfo.text; + currentPosition = labelInfo.position + labelInfo.text.length; + } + + // Ensure the label line extends to match the X-axis dash line length + // The dash line is this.maxDataPoints characters long, starting after " +" + const dashLineLength = this.maxDataPoints; + const minLabelLineLength = yAxisPadding.length + 4 + dashLineLength; // 4 for " " + if (xAxisLabels.length < minLabelLineLength) { + xAxisLabels += ' '.repeat(minLabelLineLength - xAxisLabels.length); + } output += xAxisLabels + '\n'; // Add X-axis label if provided if (this.xAxisLabel) { - const labelPadding = Math.floor((this.maxDataPoints * 2 - this.xAxisLabel.length) / 2); + // const labelPadding = Math.floor((this.maxDataPoints * 2 - this.xAxisLabel.length) / 2); // TEMP: commented for no-space test + const labelPadding = Math.floor((this.maxDataPoints - this.xAxisLabel.length) / 2); // TEMP: adjusted for no-space columns output += '\n' + yAxisPadding + ' ' + ' '.repeat(Math.max(0, labelPadding)) + this.xAxisLabel + '\n'; }