Decision Tree Visualization Tool

Decision Tree Visualization Tool

Your Decision Tree

Visualize the flow of decisions and outcomes.

Configure Decision Tree Nodes

Add, edit, or remove decision and outcome nodes. Define their relationships.

Add/Edit Node

(Optional: Describes the path from the parent to this node)
ID Text Type Parent ID Branch Condition Actions

No nodes configured. Go to "Data Configuration" to add your decision tree.

'; return; } // Convert flat data to hierarchical data for D3 tree layout const dataMap = new Map(nodes.map(node => [node.id, { ...node }])); let treeData = null; // Find the root node (node with no parent) const rootNode = nodes.find(node => node.parentNodeId === null); if (!rootNode) { treeVisualizationContainer.innerHTML = '

Error: No root node found. Please ensure one node has "No Parent" selected in Data Configuration.

'; return; } // Build the hierarchical structure dataMap.forEach(node => { if (node.parentNodeId) { const parent = dataMap.get(node.parentNodeId); if (parent) { if (!parent.children) { parent.children = []; } parent.children.push(node); } } }); treeData = dataMap.get(rootNode.id); const margin = { top: 50, right: 90, bottom: 50, left: 90 }; const width = treeVisualizationContainer.clientWidth - margin.left - margin.right; const height = treeVisualizationContainer.clientHeight - margin.top - margin.bottom; const svg = d3.select(treeVisualizationContainer) .append("svg") .attr("width", width + margin.left + margin.right) .attr("height", height + margin.top + margin.bottom) .append("g") .attr("transform", `translate(${margin.left},${margin.top})`); const treemap = d3.tree().size([width, height]); let root = d3.hierarchy(treeData, d => d.children); root = treemap(root); // Add links (paths) const link = svg.selectAll(".link") .data(root.links()) .enter().append("path") .attr("class", "link") .attr("d", d3.linkVertical() .x(d => d.x) .y(d => d.y)); // Add link labels (branch conditions) svg.selectAll(".link-text") .data(root.links()) .enter().append("text") .attr("class", "link-text") .attr("x", d => (d.source.x + d.target.x) / 2) .attr("y", d => (d.source.y + d.target.y) / 2) .attr("dy", -5) // Adjust position slightly above the line .text(d => d.target.data.branchCondition || ''); // Add nodes const node = svg.selectAll(".node") .data(root.descendants()) .enter().append("g") .attr("class", d => "node" + (d.children ? " node--internal" : " node--leaf")) .attr("transform", d => `translate(${d.x},${d.y})`); node.append("circle") .attr("r", 10) .style("fill", d => d.data.type === 'Decision' ? '#007bff' : '#28a745'); // Blue for Decision, Green for Outcome node.append("text") .attr("dy", ".35em") .attr("y", d => d.children ? -20 : 20) // Position text above for internal, below for leaf .attr("text-anchor", "middle") .text(d => d.data.text); } // --- PDF Download Function --- /** * Generates and downloads a PDF of the current dashboard content. */ window.downloadPdf = async function() { if (!dashboardTab) { console.error('Error: Dashboard tab content not found for PDF generation.'); return; } // Temporarily hide elements not needed in PDF const elementsToHide = document.querySelectorAll('.tab-nav, .nav-buttons, #downloadPdfButton'); elementsToHide.forEach(el => el.style.display = 'none'); // Ensure the dashboard tab is active for capture dashboardTab.classList.add('active'); // Capture the visualization container const vizCanvas = await html2canvas(treeVisualizationContainer, { scale: 2, // Increase scale for better resolution in PDF useCORS: true, logging: false, scrollX: -window.scrollX, // Capture current scroll position scrollY: -window.scrollY, windowWidth: document.documentElement.offsetWidth, windowHeight: document.documentElement.offsetHeight }); const vizImgData = vizCanvas.toDataURL('image/png'); // Re-show hidden elements immediately after capture elementsToHide.forEach(el => el.style.display = ''); const { jsPDF } = window.jspdf; const pdf = new jsPDF({ orientation: 'landscape', // Landscape for wider trees unit: 'px', format: 'a4' }); const pdfWidth = pdf.internal.pageSize.getWidth(); let yOffset = 40; // Add title to PDF pdf.setFontSize(22); pdf.text("Decision Tree Visualization Report", pdfWidth / 2, yOffset, { align: 'center' }); yOffset += 20; // Add current date/time to PDF pdf.setFontSize(10); pdf.text(`Generated on: ${new Date().toLocaleString()}`, pdfWidth / 2, yOffset, { align: 'center' }); yOffset += 40; // Add Visualization Image pdf.setFontSize(18); pdf.text("Decision Tree Diagram", pdfWidth / 2, yOffset, { align: 'center' }); yOffset += 10; const vizImgHeight = (vizCanvas.height * (pdfWidth - 40)) / vizCanvas.width; // Ensure image fits on page, add new page if needed if (yOffset + vizImgHeight > pdf.internal.pageSize.getHeight() - 20) { pdf.addPage(); yOffset = 40; // Reset yOffset for new page } pdf.addImage(vizImgData, 'PNG', 20, yOffset, pdfWidth - 40, vizImgHeight); yOffset += vizImgHeight + 30; // Add a section for detailed node data (tabular format) pdf.addPage(); pdf.setFontSize(18); pdf.text("Detailed Node Information", pdf.internal.pageSize.getWidth() / 2, 40, { align: 'center' }); const tableData = nodes.map(n => [ n.id, n.text, n.type, n.parentNodeId || 'N/A (Root)', n.branchCondition || 'N/A' ]); pdf.autoTable({ head: [['ID', 'Text', 'Type', 'Parent ID', 'Branch Condition']], body: tableData, startY: 60, theme: 'grid', styles: { fontSize: 9, cellPadding: 4 }, headStyles: { fillColor: [242, 242, 242], textColor: [51, 51, 51], fontStyle: 'bold' }, alternateRowStyles: { fillColor: [251, 251, 251] }, margin: { top: 70, left: 20, right: 20 } }); pdf.save('decision_tree_report.pdf'); }; // --- Event Listeners and Initial Render --- downloadPdfButton.addEventListener('click', downloadPdf); parentNodeIdSelect.addEventListener('change', toggleParentSelect); // Listen for changes on parent select // Initial setup updateConfigTable(); populateParentNodeSelect(); renderDecisionTreeVisualization(); updateNavigationButtons(); // Set initial button states toggleParentSelect(); // Initial call to set form field visibility });
Scroll to Top