Error: Charting libraries could not be loaded. Please refresh.
`;
return;
}
// --- Event Listeners ---
plotBtn.addEventListener('click', generatePlot);
pdfBtn.addEventListener('click', generatePdf);
// Initial plot on load with example data
generatePlot();
// --- Core Functions ---
function parseData() {
const lines = dataInput.value.split('\n').filter(line => line.trim() !== '' && !line.trim().startsWith('#'));
const data = { x: [], y: [], z: [], raw: [] };
lines.forEach(line => {
const parts = line.split(',').map(val => parseFloat(val.trim()));
if (parts.length === 3 && !parts.some(isNaN)) {
data.x.push(parts[0]);
data.y.push(parts[1]);
data.z.push(parts[2]);
data.raw.push(parts);
}
});
return data;
}
function generatePlot() {
const data = parseData();
if (data.x.length === 0) {
alert('No valid data found. Please ensure data is in X,Y,Z format.');
return;
}
placeholder.style.display = 'none';
const trace = {
x: data.x,
y: data.y,
z: data.z,
mode: 'markers',
type: 'scatter3d',
marker: {
color: colorInput.value,
size: parseInt(sizeInput.value, 10),
},
hoverinfo: 'x+y+z',
hovertemplate: `
${xLabelInput.value}: %{x}
${yLabelInput.value}: %{y}
${zLabelInput.value}: %{z}
`
};
const layout = {
title: {
text: titleInput.value,
font: {
family: 'Roboto, sans-serif',
size: 20
}
},
scene: {
xaxis: { title: xLabelInput.value },
yaxis: { title: yLabelInput.value },
zaxis: { title: zLabelInput.value }
},
margin: { l: 0, r: 0, b: 0, t: 40 }
};
Plotly.newPlot(plotArea, [trace], layout, { responsive: true });
pdfBtn.disabled = false;
}
async function generatePdf() {
pdfBtn.textContent = 'Generating PDF...';
pdfBtn.disabled = true;
const { jsPDF } = window.jspdf;
const doc = new jsPDF('p', 'mm', 'a4');
const primaryColor = getComputedStyle(document.documentElement).getPropertyValue('--plot-primary-color').trim();
// --- Page 1: Plot Image ---
doc.setFontSize(22);
doc.setFont('helvetica', 'bold');
doc.setTextColor(primaryColor);
doc.text('3D Scatter Plot Report', doc.internal.pageSize.getWidth() / 2, 20, { align: 'center' });
doc.setFontSize(14);
doc.setFont('helvetica', 'normal');
doc.setTextColor('#333333');
doc.text(titleInput.value, doc.internal.pageSize.getWidth() / 2, 30, { align: 'center' });
try {
const imgData = await Plotly.toImage(plotArea, { format: 'png', width: 800, height: 600 });
doc.addImage(imgData, 'PNG', 15, 40, 180, 135);
} catch(e) {
console.error("PDF Image generation failed:", e);
doc.text("Could not generate plot image.", 15, 60);
}
// --- Page 2: Data Table ---
doc.addPage();
doc.setFontSize(18);
doc.setFont('helvetica', 'bold');
doc.setTextColor(primaryColor);
doc.text('Raw Data Table', doc.internal.pageSize.getWidth() / 2, 20, { align: 'center' });
const data = parseData();
const tableHeaders = [xLabelInput.value, yLabelInput.value, zLabelInput.value];
doc.autoTable({
head: [tableHeaders],
body: data.raw,
startY: 30,
theme: 'grid',
headStyles: {
fillColor: primaryColor,
textColor: '#ffffff',
fontStyle: 'bold'
},
styles: {
font: 'Roboto',
cellPadding: 3,
fontSize: 10
}
});
doc.save(`${titleInput.value.replace(/ /g, '_')}_Report.pdf`);
pdfBtn.textContent = 'Download PDF Report';
pdfBtn.disabled = false;
}
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', setupScatterPlotter);
} else {
setupScatterPlotter();
}