<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Helios Chart Visualization</title>
<style>
body {
margin: 0;
padding: 20px;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
background: #1a1a1a;
color: #ffffff;
}
.container {
max-width: 1200px;
margin: 0 auto;
}
.header {
text-align: center;
margin-bottom: 30px;
}
.chart-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(400px, 1fr));
gap: 20px;
margin-bottom: 30px;
}
.chart-container {
background: #2a2a2a;
border: 1px solid #333;
border-radius: 8px;
padding: 20px;
position: relative;
}
.chart-title {
font-size: 18px;
font-weight: bold;
margin-bottom: 15px;
color: #00d4ff;
}
.chart-canvas {
width: 100%;
height: 300px;
background: #1e1e1e;
border: 1px solid #444;
border-radius: 4px;
cursor: crosshair;
}
.chart-info {
margin-top: 10px;
font-size: 12px;
color: #888;
}
.controls {
background: #2a2a2a;
border: 1px solid #333;
border-radius: 8px;
padding: 20px;
margin-bottom: 20px;
}
.control-group {
margin-bottom: 15px;
}
.control-group label {
display: block;
margin-bottom: 5px;
font-weight: bold;
}
.control-group input, .control-group select {
width: 100%;
padding: 8px;
background: #1e1e1e;
border: 1px solid #444;
border-radius: 4px;
color: #fff;
}
.control-group button {
background: #00d4ff;
color: #000;
border: none;
padding: 10px 20px;
border-radius: 4px;
cursor: pointer;
font-weight: bold;
}
.control-group button:hover {
background: #00b8e6;
}
.status {
background: #2a2a2a;
border: 1px solid #333;
border-radius: 8px;
padding: 20px;
margin-bottom: 20px;
}
.status-item {
margin-bottom: 10px;
}
.status-label {
font-weight: bold;
color: #00d4ff;
}
.status-value {
color: #fff;
}
.error {
color: #ff6b6b;
}
.success {
color: #51cf66;
}
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1>🚀 Helios Chart Visualization</h1>
<p>Real-time data visualization with WebGPU acceleration</p>
</div>
<div class="status" id="status">
<div class="status-item">
<span class="status-label">WASM Status:</span>
<span class="status-value" id="wasm-status">Loading...</span>
</div>
<div class="status-item">
<span class="status-label">WebGPU Support:</span>
<span class="status-value" id="webgpu-status">Checking...</span>
</div>
<div class="status-item">
<span class="status-label">Chart Renderer:</span>
<span class="status-value" id="renderer-status">Initializing...</span>
</div>
</div>
<div class="controls">
<h3>Chart Controls</h3>
<div class="control-group">
<label for="chart-type">Chart Type:</label>
<select id="chart-type">
<option value="line">Line Chart</option>
<option value="bar">Bar Chart</option>
<option value="scatter">Scatter Plot</option>
<option value="heatmap">Heatmap</option>
</select>
</div>
<div class="control-group">
<label for="data-points">Data Points:</label>
<input type="number" id="data-points" value="10" min="5" max="100">
</div>
<div class="control-group">
<button onclick="generateChart()">Generate Chart</button>
<button onclick="startStreaming()">Start Streaming</button>
<button onclick="stopStreaming()">Stop Streaming</button>
</div>
</div>
<div class="chart-grid">
<div class="chart-container">
<div class="chart-title">Line Chart</div>
<canvas id="line-chart" class="chart-canvas"></canvas>
<div class="chart-info">Interactive line chart with pan/zoom</div>
</div>
<div class="chart-container">
<div class="chart-title">Bar Chart</div>
<canvas id="bar-chart" class="chart-canvas"></canvas>
<div class="chart-info">Animated bar chart</div>
</div>
<div class="chart-container">
<div class="chart-title">Scatter Plot</div>
<canvas id="scatter-chart" class="chart-canvas"></canvas>
<div class="chart-info">Interactive scatter plot</div>
</div>
<div class="chart-container">
<div class="chart-title">Heatmap</div>
<canvas id="heatmap-chart" class="chart-canvas"></canvas>
<div class="chart-info">Real-time heatmap</div>
</div>
</div>
</div>
<script type="module">
let wasmBindings = null;
let streamingInterval = null;
let charts = {};
window.addEventListener('TrunkApplicationStarted', async (event) => {
try {
wasmBindings = window.wasmBindings;
updateStatus('wasm-status', 'Loaded successfully', 'success');
const webgpuSupport = wasmBindings.test_webgpu_support();
updateStatus('webgpu-status', webgpuSupport ? 'Supported' : 'Not Supported',
webgpuSupport ? 'success' : 'error');
updateStatus('renderer-status', 'Ready', 'success');
initializeCharts();
generateChart();
} catch (error) {
console.error('Error initializing visualization:', error);
updateStatus('wasm-status', 'Failed to load', 'error');
}
});
function updateStatus(elementId, text, className) {
const element = document.getElementById(elementId);
element.textContent = text;
element.className = `status-value ${className}`;
}
function initializeCharts() {
const chartTypes = ['line', 'bar', 'scatter', 'heatmap'];
chartTypes.forEach(type => {
const canvas = document.getElementById(`${type}-chart`);
const ctx = canvas.getContext('2d');
canvas.width = canvas.offsetWidth * window.devicePixelRatio;
canvas.height = canvas.offsetHeight * window.devicePixelRatio;
ctx.scale(window.devicePixelRatio, window.devicePixelRatio);
charts[type] = {
canvas: canvas,
ctx: ctx,
data: generateSampleData(type, 10),
config: getChartConfig(type)
};
addInteractionHandlers(canvas, type);
});
}
function generateSampleData(type, count) {
const data = [];
for (let i = 0; i < count; i++) {
switch (type) {
case 'line':
case 'bar':
data.push({
x: i,
y: Math.sin(i * 0.5) * 50 + 50 + Math.random() * 20
});
break;
case 'scatter':
data.push({
x: Math.random() * 100,
y: Math.random() * 100
});
break;
case 'heatmap':
data.push({
x: i % 10,
y: Math.floor(i / 10),
value: Math.random() * 100
});
break;
}
}
return data;
}
function getChartConfig(type) {
const baseConfig = {
width: 400,
height: 300,
padding: 20,
colors: ['#00d4ff', '#51cf66', '#ffd43b', '#ff6b6b', '#9775fa']
};
switch (type) {
case 'line':
return { ...baseConfig, lineWidth: 2, showPoints: true };
case 'bar':
return { ...baseConfig, barWidth: 20, barSpacing: 5 };
case 'scatter':
return { ...baseConfig, pointSize: 4, showGrid: true };
case 'heatmap':
return { ...baseConfig, cellSize: 20, colorScale: 'viridis' };
default:
return baseConfig;
}
}
function renderChart(type) {
const chart = charts[type];
if (!chart) return;
const { ctx, data, config } = chart;
const { width, height, padding } = config;
ctx.fillStyle = '#1e1e1e';
ctx.fillRect(0, 0, width, height);
drawGrid(ctx, width, height, padding);
switch (type) {
case 'line':
drawLineChart(ctx, data, config);
break;
case 'bar':
drawBarChart(ctx, data, config);
break;
case 'scatter':
drawScatterChart(ctx, data, config);
break;
case 'heatmap':
drawHeatmap(ctx, data, config);
break;
}
}
function drawGrid(ctx, width, height, padding) {
ctx.strokeStyle = '#333';
ctx.lineWidth = 1;
for (let x = padding; x < width - padding; x += 40) {
ctx.beginPath();
ctx.moveTo(x, padding);
ctx.lineTo(x, height - padding);
ctx.stroke();
}
for (let y = padding; y < height - padding; y += 40) {
ctx.beginPath();
ctx.moveTo(padding, y);
ctx.lineTo(width - padding, y);
ctx.stroke();
}
}
function drawLineChart(ctx, data, config) {
if (data.length < 2) return;
const { width, height, padding, colors } = config;
const chartWidth = width - 2 * padding;
const chartHeight = height - 2 * padding;
ctx.strokeStyle = colors[0];
ctx.lineWidth = config.lineWidth || 2;
ctx.beginPath();
data.forEach((point, index) => {
const x = padding + (point.x / (data.length - 1)) * chartWidth;
const y = padding + (1 - point.y / 100) * chartHeight;
if (index === 0) {
ctx.moveTo(x, y);
} else {
ctx.lineTo(x, y);
}
});
ctx.stroke();
if (config.showPoints) {
ctx.fillStyle = colors[0];
data.forEach(point => {
const x = padding + (point.x / (data.length - 1)) * chartWidth;
const y = padding + (1 - point.y / 100) * chartHeight;
ctx.beginPath();
ctx.arc(x, y, 3, 0, 2 * Math.PI);
ctx.fill();
});
}
}
function drawBarChart(ctx, data, config) {
const { width, height, padding, colors } = config;
const chartWidth = width - 2 * padding;
const chartHeight = height - 2 * padding;
const barWidth = config.barWidth || 20;
const barSpacing = config.barSpacing || 5;
const totalBarWidth = barWidth + barSpacing;
const maxBars = Math.floor(chartWidth / totalBarWidth);
const visibleData = data.slice(0, maxBars);
visibleData.forEach((point, index) => {
const x = padding + index * totalBarWidth;
const barHeight = (point.y / 100) * chartHeight;
const y = padding + chartHeight - barHeight;
ctx.fillStyle = colors[index % colors.length];
ctx.fillRect(x, y, barWidth, barHeight);
});
}
function drawScatterChart(ctx, data, config) {
const { width, height, padding, colors } = config;
const chartWidth = width - 2 * padding;
const chartHeight = height - 2 * padding;
ctx.fillStyle = colors[0];
data.forEach(point => {
const x = padding + (point.x / 100) * chartWidth;
const y = padding + (1 - point.y / 100) * chartHeight;
ctx.beginPath();
ctx.arc(x, y, config.pointSize || 4, 0, 2 * Math.PI);
ctx.fill();
});
}
function drawHeatmap(ctx, data, config) {
const { width, height, padding, colors } = config;
const chartWidth = width - 2 * padding;
const chartHeight = height - 2 * padding;
const cellSize = config.cellSize || 20;
const maxValue = Math.max(...data.map(d => d.value));
data.forEach(point => {
const x = padding + point.x * cellSize;
const y = padding + point.y * cellSize;
const intensity = point.value / maxValue;
const colorIndex = Math.floor(intensity * (colors.length - 1));
ctx.fillStyle = colors[colorIndex];
ctx.fillRect(x, y, cellSize, cellSize);
});
}
function addInteractionHandlers(canvas, type) {
let isDragging = false;
let lastX = 0;
let lastY = 0;
canvas.addEventListener('mousedown', (e) => {
isDragging = true;
lastX = e.offsetX;
lastY = e.offsetY;
});
canvas.addEventListener('mousemove', (e) => {
if (isDragging) {
const deltaX = e.offsetX - lastX;
const deltaY = e.offsetY - lastY;
console.log(`Panning ${type} chart:`, { deltaX, deltaY });
lastX = e.offsetX;
lastY = e.offsetY;
}
});
canvas.addEventListener('mouseup', () => {
isDragging = false;
});
canvas.addEventListener('wheel', (e) => {
e.preventDefault();
const delta = e.deltaY > 0 ? 0.9 : 1.1;
console.log(`Zooming ${type} chart:`, delta);
});
}
window.generateChart = function() {
const chartType = document.getElementById('chart-type').value;
const dataPoints = parseInt(document.getElementById('data-points').value);
Object.keys(charts).forEach(type => {
charts[type].data = generateSampleData(type, dataPoints);
renderChart(type);
});
};
window.startStreaming = function() {
if (streamingInterval) return;
streamingInterval = setInterval(() => {
Object.keys(charts).forEach(type => {
const chart = charts[type];
const newPoint = generateSampleData(type, 1)[0];
chart.data.push(newPoint);
if (chart.data.length > 20) {
chart.data.shift();
}
renderChart(type);
});
}, 1000);
};
window.stopStreaming = function() {
if (streamingInterval) {
clearInterval(streamingInterval);
streamingInterval = null;
}
};
</script>
</body>
</html>