Defined Algorithm Parameters
`;
parameters.forEach((param, index) => {
html += `
P-${index + 1}: ${param.name} (${param.symbol})
Source/Unit: ${param.source} / ${param.unit}
${param.description || 'No description provided.'}
${param.value.replace(/\n/g, '
')}
`;
});
html += `
`;
reviewContent.innerHTML = html;
pdfDownloadBtn.disabled = false;
switchTab(2); // Switch to review tab
};
/**
* PDF Generation Function
*/
const downloadPDF = () => {
const metaData = getMetaFormData();
const jsPDF = window.jspdf.jsPDF;
const doc = new jsPDF('p', 'pt', 'a4');
let currentY = 40;
const margin = 40;
const pageWidth = doc.internal.pageSize.width;
const maxWidth = pageWidth - (margin * 2);
const checkPageBreak = (spaceNeeded) => {
if (currentY + spaceNeeded > doc.internal.pageSize.height - margin) {
doc.addPage();
currentY = margin;
}
};
const addSectionHeader = (title, color = [0, 0, 0]) => {
checkPageBreak(25);
doc.setFontSize(16);
doc.setFont('Helvetica', 'bold');
doc.setTextColor(color[0], color[1], color[2]);
doc.text(title, margin, currentY);
currentY += 10;
doc.setLineWidth(0.5);
doc.setDrawColor(200);
doc.line(margin, currentY, pageWidth - margin, currentY);
currentY += 12;
doc.setTextColor(0);
};
const addText = (text, size = 10, style = 'normal', indent = 0) => {
doc.setFontSize(size);
doc.setFont('Helvetica', style);
const lines = doc.splitTextToSize(text, maxWidth - indent);
checkPageBreak(lines.length * 9);
doc.text(lines, margin + indent, currentY);
currentY += (lines.length * 9);
};
const addKeyValue = (key, value) => {
doc.setFontSize(10);
doc.setFont('Helvetica', 'bold');
doc.text(`${key}:`, margin + 10, currentY);
doc.setFont('Helvetica', 'normal');
const keyWidth = doc.getStringUnitWidth(`${key}: `) * 10;
const valueLines = doc.splitTextToSize(value, maxWidth - keyWidth - 10);
// Draw first line of value immediately after key
doc.text(valueLines[0], margin + 10 + keyWidth, currentY);
currentY += 10;
// Draw remaining lines of value with proper indent
for(let i = 1; i < valueLines.length; i++) {
doc.text(valueLines[i], margin + 10 + keyWidth, currentY);
currentY += 10;
}
currentY += 5;
};
// --- PDF Content ---
// Title Block
doc.setFontSize(20);
doc.setFont('Helvetica', 'bold');
doc.setTextColor(0, 119, 182);
doc.text(`SENSOR FUSION PARAMETER SHEET`, pageWidth / 2, currentY, { align: 'center' });
currentY += 15;
doc.setFontSize(14);
doc.text(metaData.project_name, pageWidth / 2, currentY, { align: 'center' });
currentY += 20;
// 1. Context
addSectionHeader("1. Project & Algorithm Context", [0, 119, 182]);
addKeyValue("Algorithm Type", metaData.algo_name);
addKeyValue("Version", metaData.algo_version);
addKeyValue("Time Step (dt)", `${metaData.time_step} seconds`);
addKeyValue("Primary Sensors", metaData.primary_sensors);
currentY += 5;
addText("Purpose / State Vector:", 10, 'bold');
addText(metaData.algo_purpose, 10, 'normal', 10);
currentY += 10;
// 2. Parameters Table
addSectionHeader("2. Defined Algorithm Parameters", [0, 119, 182]);
if (parameters.length > 0) {
const paramHead = [
["ID", "Symbol", "Name", "Source/Unit", "Value & Description"]
];
const paramBody = parameters.map((param, index) => {
const combinedValue = `VALUE:\n${param.value}\n\nDESCRIPTION:\n${param.description}`;
return [
`P-${index + 1}`,
param.symbol,
param.name,
`${param.source} / ${param.unit}`,
combinedValue
];
});
doc.autoTable({
startY: currentY,
head: paramHead,
body: paramBody,
theme: 'grid',
headStyles: { fillColor: [74, 78, 105], textColor: [255] }, // Secondary color
styles: { fontSize: 8, cellPadding: 5, font: 'Helvetica' },
columnStyles: {
0: { cellWidth: 30 },
4: { cellWidth: 150, font: 'Courier' } // Monospace font for matrix data
}
});
currentY = doc.autoTable.previous.finalY + 10;
} else {
addText("No parameters defined.", 11, 'italic', 5);
currentY += 10;
}
doc.save(`${metaData.project_name.replace(/\s/g, '_')}_Fusion_Parameters.pdf`);
};
// --- Tab Navigation ---
const switchTab = (tabIndex) => {
tabs.forEach((tab, index) => {
tab.classList.toggle('active', index === tabIndex);
contents[index].classList.toggle('active', index === tabIndex);
});
currentTab = tabIndex;
updateNavButtons();
if (tabIndex === 2) {
renderReviewSheet();
}
}
const updateNavButtons = () => {
prevBtn.disabled = currentTab === 0;
nextBtn.disabled = currentTab === tabs.length - 1;
}
tabs.forEach((tab, index) => {
tab.addEventListener('click', () => {
const tabNode = tab.closest('.fusion-tab-button');
const newIndex = Array.from(tabNode.parentNode.children).indexOf(tabNode);
switchTab(newIndex);
});
});
nextBtn.addEventListener('click', () => { if (currentTab < tabs.length - 1) switchTab(currentTab + 1); });
prevBtn.addEventListener('click', () => { if (currentTab > 0) switchTab(currentTab - 1); });
// --- Event Listeners and Initial Load ---
metaSaveBtn.addEventListener('click', updateMetadata);
pdfDownloadBtn.addEventListener('click', downloadPDF);
// Initial Setup
renderParameterList();
// Populate metadata form with default data
document.getElementById('algo-name').value = metadata.algo_name;
document.getElementById('project-name').value = metadata.project_name;
document.getElementById('algo-version').value = metadata.algo_version;
document.getElementById('time-step').value = metadata.time_step;
document.getElementById('primary-sensors').value = metadata.primary_sensors;
document.getElementById('algo-purpose').value = metadata.algo_purpose;
updateNavButtons();
});