add radar graph

This commit is contained in:
overcuriousity
2026-01-17 23:24:43 +01:00
parent 1b2de520d4
commit f02e48f4f9
2 changed files with 262 additions and 7 deletions

View File

@@ -81,7 +81,7 @@
.scale-toggle {
position: absolute;
top: 30px;
right: 140px;
right: 180px;
background: var(--border-color);
border: none;
padding: 10px 20px;
@@ -408,6 +408,92 @@
.tooltiptext strong {
color: #667eea;
}
.chart-type-toggle {
display: flex;
gap: 5px;
background: var(--border-color);
border-radius: 8px;
padding: 4px;
}
.chart-type-btn {
padding: 8px 16px;
border: none;
border-radius: 6px;
cursor: pointer;
font-size: 0.9em;
transition: all 0.3s;
background: transparent;
color: var(--text-primary);
}
.chart-type-btn:hover {
background: rgba(102, 126, 234, 0.2);
}
.chart-type-btn.active {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
}
.model-toggles {
display: flex;
flex-wrap: wrap;
gap: 10px;
margin-top: 15px;
padding: 15px;
background: var(--stat-card-bg);
border-radius: 10px;
}
.model-toggle-item {
display: flex;
align-items: center;
gap: 8px;
padding: 6px 12px;
background: var(--card-bg);
border-radius: 20px;
cursor: pointer;
transition: all 0.3s;
border: 2px solid transparent;
}
.model-toggle-item:hover {
border-color: var(--text-secondary);
}
.model-toggle-item.active {
border-color: currentColor;
}
.model-toggle-checkbox {
width: 18px;
height: 18px;
cursor: pointer;
accent-color: #667eea;
}
.model-toggle-label {
font-size: 0.9em;
cursor: pointer;
user-select: none;
}
.model-color-indicator {
width: 12px;
height: 12px;
border-radius: 50%;
flex-shrink: 0;
}
.radar-controls {
display: none;
}
.radar-controls.visible {
display: block;
}
</style>
</head>
<body>
@@ -473,6 +559,16 @@
<select id="categorySelect" onchange="updateCategoryChart()">
<option value="">Loading categories...</option>
</select>
<div class="chart-type-toggle">
<button class="chart-type-btn active" onclick="setCategoryChartType('bar')" id="barChartBtn">📊 Bar Chart</button>
<button class="chart-type-btn" onclick="setCategoryChartType('radar')" id="radarChartBtn">🕸️ Spider Web</button>
</div>
</div>
<div class="radar-controls" id="radarModelToggles">
<p style="margin-bottom: 10px; color: var(--text-secondary); font-size: 0.9em;">Toggle models to show/hide on the spider web chart:</p>
<div class="model-toggles" id="modelToggleContainer">
<!-- Model toggles will be populated dynamically -->
</div>
</div>
<div class="chart-container">
<canvas id="categoryChart"></canvas>
@@ -924,13 +1020,91 @@
});
if (comparisonData.categories.length > 0) {
initializeModelToggles();
updateCategoryChart();
}
}
// Category chart type state
let categoryChartType = 'bar';
let selectedModelsForRadar = {};
// Model colors for radar chart
const modelColors = [
{ bg: 'rgba(102, 126, 234, 0.3)', border: 'rgba(102, 126, 234, 1)' },
{ bg: 'rgba(118, 75, 162, 0.3)', border: 'rgba(118, 75, 162, 1)' },
{ bg: 'rgba(16, 185, 129, 0.3)', border: 'rgba(16, 185, 129, 1)' },
{ bg: 'rgba(245, 158, 11, 0.3)', border: 'rgba(245, 158, 11, 1)' },
{ bg: 'rgba(239, 68, 68, 0.3)', border: 'rgba(239, 68, 68, 1)' },
{ bg: 'rgba(59, 130, 246, 0.3)', border: 'rgba(59, 130, 246, 1)' },
{ bg: 'rgba(236, 72, 153, 0.3)', border: 'rgba(236, 72, 153, 1)' },
{ bg: 'rgba(34, 197, 94, 0.3)', border: 'rgba(34, 197, 94, 1)' },
{ bg: 'rgba(168, 85, 247, 0.3)', border: 'rgba(168, 85, 247, 1)' },
{ bg: 'rgba(251, 146, 60, 0.3)', border: 'rgba(251, 146, 60, 1)' }
];
function initializeModelToggles() {
if (!comparisonData) return;
const models = Object.keys(comparisonData.models);
const container = document.getElementById('modelToggleContainer');
container.innerHTML = '';
models.forEach((model, index) => {
const colorIndex = index % modelColors.length;
selectedModelsForRadar[model] = true; // All selected by default
const item = document.createElement('label');
item.className = 'model-toggle-item active';
item.style.color = modelColors[colorIndex].border;
item.innerHTML = `
<span class="model-color-indicator" style="background: ${modelColors[colorIndex].border}"></span>
<input type="checkbox" class="model-toggle-checkbox" checked
onchange="toggleModelVisibility('${model}', this.checked, this.parentElement)">
<span class="model-toggle-label">${model}</span>
`;
container.appendChild(item);
});
}
function toggleModelVisibility(model, isVisible, element) {
selectedModelsForRadar[model] = isVisible;
element.classList.toggle('active', isVisible);
updateCategoryChart();
}
function setCategoryChartType(type) {
categoryChartType = type;
// Update button states
document.getElementById('barChartBtn').classList.toggle('active', type === 'bar');
document.getElementById('radarChartBtn').classList.toggle('active', type === 'radar');
// Show/hide radar controls
document.getElementById('radarModelToggles').classList.toggle('visible', type === 'radar');
// Show/hide category selector (hide for radar since it shows all categories)
document.getElementById('categorySelect').style.display = type === 'radar' ? 'none' : 'block';
updateCategoryChart();
}
function updateCategoryChart() {
if (!comparisonData) return;
const ctx = document.getElementById('categoryChart');
if (window.categoryChartInstance) {
window.categoryChartInstance.destroy();
}
if (categoryChartType === 'radar') {
updateRadarChart(ctx);
} else {
updateBarChart(ctx);
}
}
function updateBarChart(ctx) {
const category = document.getElementById('categorySelect').value;
const models = Object.keys(comparisonData.models);
@@ -939,11 +1113,6 @@
return stats ? stats.average : 0;
});
const ctx = document.getElementById('categoryChart');
if (window.categoryChartInstance) {
window.categoryChartInstance.destroy();
}
window.categoryChartInstance = new Chart(ctx, {
type: 'bar',
data: {
@@ -964,6 +1133,92 @@
});
}
function updateRadarChart(ctx) {
const categories = comparisonData.categories;
const models = Object.keys(comparisonData.models);
// Create datasets for each selected model
const datasets = [];
let allData = [];
models.forEach((model, index) => {
if (!selectedModelsForRadar[model]) return;
const colorIndex = index % modelColors.length;
const data = categories.map(category => {
const stats = comparisonData.models[model].category_stats[category];
return stats ? stats.average : 0;
});
allData = allData.concat(data);
datasets.push({
label: model,
data: data,
backgroundColor: modelColors[colorIndex].bg,
borderColor: modelColors[colorIndex].border,
borderWidth: 2,
pointBackgroundColor: modelColors[colorIndex].border,
pointBorderColor: '#fff',
pointHoverBackgroundColor: '#fff',
pointHoverBorderColor: modelColors[colorIndex].border,
pointRadius: 4,
pointHoverRadius: 6
});
});
window.categoryChartInstance = new Chart(ctx, {
type: 'radar',
data: {
labels: categories,
datasets: datasets
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: {
position: 'top',
labels: {
padding: 20,
usePointStyle: true,
pointStyle: 'circle'
}
},
tooltip: {
callbacks: {
label: function(context) {
return `${context.dataset.label}: ${context.raw.toFixed(2)}`;
}
}
}
},
scales: {
r: {
beginAtZero: !zoomedScale,
min: zoomedScale ? Math.max(0, Math.min(...allData) - 0.5) : 0,
max: zoomedScale ? Math.min(5, Math.max(...allData) + 0.5) : 5,
ticks: {
stepSize: zoomedScale ? 0.5 : 1,
backdropColor: 'transparent'
},
grid: {
color: 'rgba(102, 126, 234, 0.2)'
},
angleLines: {
color: 'rgba(102, 126, 234, 0.2)'
},
pointLabels: {
font: {
size: 11
}
}
}
}
}
});
}
async function loadModelDetails() {
const modelName = document.getElementById('modelSelect').value;
if (!modelName || !comparisonData) return;