diff --git a/src/components/DreamCharts.tsx b/src/components/DreamCharts.tsx index 7d6dd5d..0327023 100644 --- a/src/components/DreamCharts.tsx +++ b/src/components/DreamCharts.tsx @@ -16,96 +16,101 @@ export const EEGChart: React.FC<{ chipInput: ChipInput }> = ({chipInput}) => { {name: 'Delta', values: chipInput.eeg.delta} ]; - // Clear previous chart - d3.select(chartRef.current).selectAll('*').remove(); + // Function to render the chart + const renderChart = () => { + if (!chartRef.current) return; + + // Clear previous chart + d3.select(chartRef.current).selectAll('*').remove(); - const margin = {top: 20, right: 20, bottom: 30, left: 50}; - const width = chartRef.current.clientWidth - margin.left - margin.right; - const height = chartRef.current.clientHeight - margin.top - margin.bottom; + const margin = {top: 20, right: 20, bottom: 30, left: 50}; + const width = chartRef.current.clientWidth - margin.left - margin.right; + const height = chartRef.current.clientHeight - margin.top - margin.bottom; - const svg = d3.select(chartRef.current) - .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 svg = d3.select(chartRef.current) + .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})`); - // X scale - const x = d3.scaleLinear() - .domain([0, chipInput.eeg.alpha.length - 1]) - .range([0, width]); + // X scale + const x = d3.scaleLinear() + .domain([0, chipInput.eeg.alpha.length - 1]) + .range([0, width]); - // Y scale - const y = d3.scaleLinear() - .domain([0, d3.max(eegData.flatMap(d => d.values)) || 50]) - .range([height, 0]); + // Y scale + const y = d3.scaleLinear() + .domain([0, d3.max(eegData.flatMap(d => d.values)) || 50]) + .range([height, 0]); - // Line generator - const line = d3.line() - .x((_d, i) => x(i)) - .y(d => y(d)) - .curve(d3.curveMonotoneX); + // Line generator + const line = d3.line() + .x((_d, i) => x(i)) + .y(d => y(d)) + .curve(d3.curveMonotoneX); - // Color scale - const color = d3.scaleOrdinal() - .domain(eegData.map(d => d.name)) - .range(['#8884d8', '#82ca9d', '#ffc658', '#ff8042']); + // Color scale + const color = d3.scaleOrdinal() + .domain(eegData.map(d => d.name)) + .range(['#8884d8', '#82ca9d', '#ffc658', '#ff8042']); - // Add X axis - svg.append('g') - .attr('transform', `translate(0,${height})`) - .call(d3.axisBottom(x)) - .selectAll('text') - .style('fill', 'var(--text)'); + // Add X axis + svg.append('g') + .attr('transform', `translate(0,${height})`) + .call(d3.axisBottom(x)) + .selectAll('text') + .style('fill', 'var(--text)'); - // Add Y axis - svg.append('g') - .call(d3.axisLeft(y)) - .selectAll('text') - .style('fill', 'var(--text)'); + // Add Y axis + svg.append('g') + .call(d3.axisLeft(y)) + .selectAll('text') + .style('fill', 'var(--text)'); - // Add lines - eegData.forEach(d => { - svg.append('path') - .datum(d.values) - .attr('fill', 'none') - .attr('stroke', color(d.name)) - .attr('stroke-width', 1.5) - .attr('d', line); - }); + // Add lines + eegData.forEach(d => { + svg.append('path') + .datum(d.values) + .attr('fill', 'none') + .attr('stroke', color(d.name)) + .attr('stroke-width', 1.5) + .attr('d', line); + }); - // Add legend at the top right - const legendItemHeight = 20; // Height allocated for each legend item + // Add legend at the top right + const legendItemHeight = 20; // Height allocated for each legend item - const legend = svg.append('g') - .attr('font-family', 'sans-serif') - .attr('font-size', 10) - .attr('text-anchor', 'end') - .selectAll('g') - .data(eegData) - .enter().append('g') - .attr('transform', (_d, i) => `translate(${width},${i * legendItemHeight + 10})`); + const legend = svg.append('g') + .attr('font-family', 'sans-serif') + .attr('font-size', 10) + .attr('text-anchor', 'end') + .selectAll('g') + .data(eegData) + .enter().append('g') + .attr('transform', (_d, i) => `translate(${width},${i * legendItemHeight + 10})`); - legend.append('rect') - .attr('x', -15) - .attr('width', 15) - .attr('height', 15) - .attr('fill', d => color(d.name)); + legend.append('rect') + .attr('x', -15) + .attr('width', 15) + .attr('height', 15) + .attr('fill', d => color(d.name)); - legend.append('text') - .attr('x', -20) - .attr('y', 7.5) - .attr('dy', '0.32em') - .style('fill', 'var(--text)') - .text(d => d.name); + legend.append('text') + .attr('x', -20) + .attr('y', 7.5) + .attr('dy', '0.32em') + .style('fill', 'var(--text)') + .text(d => d.name); + }; + + // Initial render + renderChart(); // Handle resize const handleResize = () => { if (!chartRef.current) return; - - // Re-render chart on resize - d3.select(chartRef.current).selectAll('*').remove(); - // We would re-render the chart here, but for simplicity we'll just reload the component + renderChart(); }; window.addEventListener('resize', handleResize); @@ -127,98 +132,103 @@ export const VitalsChart: React.FC<{ chipInput: ChipInput }> = ({chipInput}) => {name: 'HRV', values: chipInput.hrv} ]; - // Clear previous chart - d3.select(chartRef.current).selectAll('*').remove(); + // Function to render the chart + const renderChart = () => { + if (!chartRef.current) return; + + // Clear previous chart + d3.select(chartRef.current).selectAll('*').remove(); - const margin = {top: 20, right: 20, bottom: 30, left: 50}; - const width = chartRef.current.clientWidth - margin.left - margin.right; - const height = chartRef.current.clientHeight - margin.top - margin.bottom; + const margin = {top: 20, right: 20, bottom: 30, left: 50}; + const width = chartRef.current.clientWidth - margin.left - margin.right; + const height = chartRef.current.clientHeight - margin.top - margin.bottom; - const svg = d3.select(chartRef.current) - .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 svg = d3.select(chartRef.current) + .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})`); - // X scale - const x = d3.scaleLinear() - .domain([0, chipInput.puls.length - 1]) - .range([0, width]); + // X scale + const x = d3.scaleLinear() + .domain([0, chipInput.puls.length - 1]) + .range([0, width]); - // Y scale - const minValue = d3.min(vitalsData.flatMap(d => d.values)) || 0; - const maxValue = d3.max(vitalsData.flatMap(d => d.values)) || 100; - const y = d3.scaleLinear() - .domain([Math.max(0, minValue - 10), maxValue + 10]) - .range([height, 0]); + // Y scale + const minValue = d3.min(vitalsData.flatMap(d => d.values)) || 0; + const maxValue = d3.max(vitalsData.flatMap(d => d.values)) || 100; + const y = d3.scaleLinear() + .domain([Math.max(0, minValue - 10), maxValue + 10]) + .range([height, 0]); - // Line generator - const line = d3.line() - .x((_d, i) => x(i)) - .y(d => y(d)) - .curve(d3.curveMonotoneX); + // Line generator + const line = d3.line() + .x((_d, i) => x(i)) + .y(d => y(d)) + .curve(d3.curveMonotoneX); - // Color scale - const color = d3.scaleOrdinal() - .domain(vitalsData.map(d => d.name)) - .range(['#ff6b6b', '#48dbfb']); + // Color scale + const color = d3.scaleOrdinal() + .domain(vitalsData.map(d => d.name)) + .range(['#ff6b6b', '#48dbfb']); - // Add X axis - svg.append('g') - .attr('transform', `translate(0,${height})`) - .call(d3.axisBottom(x)) - .selectAll('text') - .style('fill', 'var(--text)'); + // Add X axis + svg.append('g') + .attr('transform', `translate(0,${height})`) + .call(d3.axisBottom(x)) + .selectAll('text') + .style('fill', 'var(--text)'); - // Add Y axis - svg.append('g') - .call(d3.axisLeft(y)) - .selectAll('text') - .style('fill', 'var(--text)'); + // Add Y axis + svg.append('g') + .call(d3.axisLeft(y)) + .selectAll('text') + .style('fill', 'var(--text)'); - // Add lines - vitalsData.forEach(d => { - svg.append('path') - .datum(d.values) - .attr('fill', 'none') - .attr('stroke', color(d.name)) - .attr('stroke-width', 1.5) - .attr('d', line); - }); + // Add lines + vitalsData.forEach(d => { + svg.append('path') + .datum(d.values) + .attr('fill', 'none') + .attr('stroke', color(d.name)) + .attr('stroke-width', 1.5) + .attr('d', line); + }); - // Add legend at the top right - const legendItemHeight = 20; // Height allocated for each legend item + // Add legend at the top right + const legendItemHeight = 20; // Height allocated for each legend item - const legend = svg.append('g') - .attr('font-family', 'sans-serif') - .attr('font-size', 10) - .attr('text-anchor', 'end') - .selectAll('g') - .data(vitalsData) - .enter().append('g') - .attr('transform', (_d, i) => `translate(${width},${i * legendItemHeight + 10})`); + const legend = svg.append('g') + .attr('font-family', 'sans-serif') + .attr('font-size', 10) + .attr('text-anchor', 'end') + .selectAll('g') + .data(vitalsData) + .enter().append('g') + .attr('transform', (_d, i) => `translate(${width},${i * legendItemHeight + 10})`); - legend.append('rect') - .attr('x', -15) - .attr('width', 15) - .attr('height', 15) - .attr('fill', d => color(d.name)); + legend.append('rect') + .attr('x', -15) + .attr('width', 15) + .attr('height', 15) + .attr('fill', d => color(d.name)); - legend.append('text') - .attr('x', -20) - .attr('y', 7.5) - .attr('dy', '0.32em') - .style('fill', 'var(--text)') - .text(d => d.name); + legend.append('text') + .attr('x', -20) + .attr('y', 7.5) + .attr('dy', '0.32em') + .style('fill', 'var(--text)') + .text(d => d.name); + }; + + // Initial render + renderChart(); // Handle resize const handleResize = () => { if (!chartRef.current) return; - - // Re-render chart on resize - d3.select(chartRef.current).selectAll('*').remove(); - // We would re-render the chart here, but for simplicity we'll just reload the component + renderChart(); }; window.addEventListener('resize', handleResize); @@ -239,92 +249,97 @@ export const MovementChart: React.FC<{ chipInput: ChipInput }> = ({chipInput}) = {name: 'Bewegung', values: chipInput.bewegung.map(v => v * 100)} // Scale movement for better visibility ]; - // Clear previous chart - d3.select(chartRef.current).selectAll('*').remove(); + // Function to render the chart + const renderChart = () => { + if (!chartRef.current) return; + + // Clear previous chart + d3.select(chartRef.current).selectAll('*').remove(); - const margin = {top: 20, right: 20, bottom: 30, left: 50}; - const width = chartRef.current.clientWidth - margin.left - margin.right; - const height = chartRef.current.clientHeight - margin.top - margin.bottom; + const margin = {top: 20, right: 20, bottom: 30, left: 50}; + const width = chartRef.current.clientWidth - margin.left - margin.right; + const height = chartRef.current.clientHeight - margin.top - margin.bottom; - const svg = d3.select(chartRef.current) - .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 svg = d3.select(chartRef.current) + .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})`); - // X scale - const x = d3.scaleLinear() - .domain([0, chipInput.bewegung.length - 1]) - .range([0, width]); + // X scale + const x = d3.scaleLinear() + .domain([0, chipInput.bewegung.length - 1]) + .range([0, width]); - // Y scale - const y = d3.scaleLinear() - .domain([0, d3.max(bewegungData[0].values) || 100]) - .range([height, 0]); + // Y scale + const y = d3.scaleLinear() + .domain([0, d3.max(bewegungData[0].values) || 100]) + .range([height, 0]); - // Line generator - const line = d3.line() - .x((_d, i) => x(i)) - .y(d => y(d)) - .curve(d3.curveMonotoneX); + // Line generator + const line = d3.line() + .x((_d, i) => x(i)) + .y(d => y(d)) + .curve(d3.curveMonotoneX); - // Color scale - const color = '#1dd1a1'; // Use the same color as before for consistency + // Color scale + const color = '#1dd1a1'; // Use the same color as before for consistency - // Add X axis - svg.append('g') - .attr('transform', `translate(0,${height})`) - .call(d3.axisBottom(x)) - .selectAll('text') - .style('fill', 'var(--text)'); + // Add X axis + svg.append('g') + .attr('transform', `translate(0,${height})`) + .call(d3.axisBottom(x)) + .selectAll('text') + .style('fill', 'var(--text)'); - // Add Y axis - svg.append('g') - .call(d3.axisLeft(y)) - .selectAll('text') - .style('fill', 'var(--text)'); + // Add Y axis + svg.append('g') + .call(d3.axisLeft(y)) + .selectAll('text') + .style('fill', 'var(--text)'); - // Add line - svg.append('path') - .datum(bewegungData[0].values) - .attr('fill', 'none') - .attr('stroke', color) - .attr('stroke-width', 1.5) - .attr('d', line); + // Add line + svg.append('path') + .datum(bewegungData[0].values) + .attr('fill', 'none') + .attr('stroke', color) + .attr('stroke-width', 1.5) + .attr('d', line); - // Add legend at the top right - const legendItemHeight = 20; // Height allocated for each legend item + // Add legend at the top right + const legendItemHeight = 20; // Height allocated for each legend item - const legend = svg.append('g') - .attr('font-family', 'sans-serif') - .attr('font-size', 10) - .attr('text-anchor', 'end') - .selectAll('g') - .data(bewegungData) - .enter().append('g') - .attr('transform', (_d, i) => `translate(${width},${i * legendItemHeight + 10})`); + const legend = svg.append('g') + .attr('font-family', 'sans-serif') + .attr('font-size', 10) + .attr('text-anchor', 'end') + .selectAll('g') + .data(bewegungData) + .enter().append('g') + .attr('transform', (_d, i) => `translate(${width},${i * legendItemHeight + 10})`); - legend.append('rect') - .attr('x', -15) - .attr('width', 15) - .attr('height', 15) - .attr('fill', () => color); + legend.append('rect') + .attr('x', -15) + .attr('width', 15) + .attr('height', 15) + .attr('fill', () => color); - legend.append('text') - .attr('x', -20) - .attr('y', 7.5) - .attr('dy', '0.32em') - .style('fill', 'var(--text)') - .text(d => d.name); + legend.append('text') + .attr('x', -20) + .attr('y', 7.5) + .attr('dy', '0.32em') + .style('fill', 'var(--text)') + .text(d => d.name); + }; + + // Initial render + renderChart(); // Handle resize const handleResize = () => { if (!chartRef.current) return; - - // Re-render chart on resize - d3.select(chartRef.current).selectAll('*').remove(); - // We would re-render the chart here, but for simplicity we'll just reload the component + renderChart(); }; window.addEventListener('resize', handleResize);