Skip to content

Commit 2a69a8f

Browse files
author
Ritika Mishra
committed
Refactored EEG band power graph: improved text positioning and responsiveness
1 parent f2dc792 commit 2a69a8f

File tree

3 files changed

+94
-68
lines changed

3 files changed

+94
-68
lines changed

src/components/BandPowerGraph.tsx

Lines changed: 40 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -112,32 +112,34 @@ const Graph: React.FC<GraphProps> = ({
112112
const canvas = canvasRef.current;
113113
const container = containerRef.current;
114114
if (!canvas || !container) return;
115-
115+
116116
if (currentBandPowerData.some(isNaN)) {
117117
console.error("NaN values detected in band power data");
118118
return;
119119
}
120-
120+
121121
const ctx = canvas.getContext("2d");
122122
if (!ctx) return;
123-
123+
124124
// Responsive canvas sizing
125125
const containerWidth = container.clientWidth;
126-
const containerHeight = Math.min(containerWidth * 0.5, 400); // Limit max height
126+
const containerHeight = Math.min(containerWidth * 0.5, 400);
127127
canvas.width = containerWidth;
128128
canvas.height = containerHeight;
129-
129+
130130
const width = canvas.width;
131131
const height = canvas.height;
132-
132+
133133
ctx.clearRect(0, 0, width, height);
134-
135-
// inside drawGraph:
136-
const topMargin = 30; // space for any in-canvas labels
137-
const leftMargin = width < 500 ? 50 : 60;
138-
const bottomMargin = width < 640 ? 40 : 50;
139-
const rightMargin = 20;
140-
134+
135+
// Unified padding and offset to prevent collisions
136+
const padding = Math.min(width, height) * 0.1;
137+
const yAxisLabelOffset = 30;
138+
const topMargin = padding;
139+
const leftMargin = padding + yAxisLabelOffset + 20; // more room for Y-axis label
140+
const bottomMargin = padding + 40; // title + x-axis
141+
const rightMargin = padding;
142+
141143
// Draw axes
142144
const axisColor = theme === "dark" ? "white" : "black";
143145
ctx.beginPath();
@@ -146,44 +148,44 @@ const Graph: React.FC<GraphProps> = ({
146148
ctx.lineTo(width - rightMargin, height - bottomMargin);
147149
ctx.strokeStyle = axisColor;
148150
ctx.stroke();
149-
151+
150152
const barWidth = (width - leftMargin - rightMargin) / bandNames.length;
151-
const barSpacing = barWidth * 0.3; // Space between bars
152-
153+
const barSpacing = barWidth * 0.3;
154+
153155
let minPower = 0;
154156
let maxPower = 100;
155157
if (maxPower - minPower < 1) {
156158
maxPower = minPower + 1;
157159
}
158-
160+
159161
// Draw bars
160162
currentBandPowerData.forEach((power, index) => {
161163
const x = leftMargin + index * barWidth;
162164
const normalizedHeight = Math.max(0, (power - minPower) / (maxPower - minPower));
163165
const barHeight = Math.max(0, normalizedHeight * (height - bottomMargin - topMargin));
164-
165166
const barX = x + barSpacing / 2;
166167
const barY = height - bottomMargin - barHeight;
167-
const actualBarWidth = barWidth - barSpacing * 1.5; // Make it thinner than before
168-
168+
const actualBarWidth = barWidth - barSpacing * 1.5;
169+
169170
ctx.fillStyle = bandColors[index];
170171
ctx.fillRect(barX, barY, actualBarWidth, barHeight);
171172
});
172-
173-
// Y-axis labels
173+
174+
// Font sizing responsive
175+
const fontSize = width < 640 ? 12 : width < 768 ? 14 : width < 1024 ? 16 : 18;
174176
ctx.fillStyle = axisColor;
175-
const fontSize = width < 640 ? 10 : 12; // Smaller text on mobile
176177
ctx.font = `${fontSize}px Arial`;
177178
ctx.textAlign = "right";
178179
ctx.textBaseline = "middle";
180+
179181
const yLabelCount = Math.min(5, Math.floor(height / 50));
180182
for (let i = 0; i <= yLabelCount; i++) {
181183
const value = minPower + (maxPower - minPower) * (i / yLabelCount);
182184
const labelY = height - bottomMargin - (i / yLabelCount) * (height - bottomMargin - topMargin);
183-
ctx.fillText(value.toFixed(1), leftMargin - 5, labelY);
185+
ctx.fillText(value.toFixed(1), leftMargin - 10, labelY);
184186
}
185-
186-
// X-axis labels, centered under each actual bar
187+
188+
// X-axis labels
187189
ctx.textAlign = "center";
188190
ctx.textBaseline = "top";
189191
bandNames.forEach((band, index) => {
@@ -192,23 +194,26 @@ const Graph: React.FC<GraphProps> = ({
192194
const labelX = barX + barW / 2;
193195
ctx.fillText(band, labelX, height - bottomMargin + 5);
194196
});
195-
196-
// Title
197-
ctx.font = "1.2em Arial";
197+
198+
// Title: EEG Band Power below graph area with consistent padding
199+
ctx.font = `${fontSize + 2}px Arial`;
198200
ctx.textAlign = "center";
199-
ctx.fillText("EEG Band Power", (width + leftMargin) / 2, height - 17);
200-
201-
// Power (Y-axis name) inside canvas
201+
ctx.textBaseline = "top";
202+
ctx.fillText("EEG Band Power", width / 2, height - padding + 6);
203+
204+
// Y-axis label: Power, fully visible with outer padding
202205
ctx.save();
203206
ctx.rotate(-Math.PI / 2);
204-
ctx.font = "1.2em Arial";
207+
ctx.font = `${fontSize + 2}px Arial`;
205208
ctx.textAlign = "center";
206-
ctx.fillText("Power", -height / 2, 15);
209+
ctx.textBaseline = "bottom";
210+
ctx.fillText("Power", -height / 2, padding);
207211
ctx.restore();
208212
},
209213
[theme, bandColors, bandNames]
210214
);
211-
215+
216+
212217

213218
// Rest of the component remains the same (animateGraph, useEffect hooks)
214219
const animateGraph = useCallback(() => {

src/components/CandleLit.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ const BrightCandleView: React.FC<BrightCandleViewProps> = ({ fftData = [], betaP
4848
<svg
4949
xmlns="http://www.w3.org/2000/svg"
5050
viewBox="0 0 200 300"
51-
className={`absolute ${isFullPage ? '-top-[42%] sm:-top-[40%] md:-top-[50%] lg:-top-[42%]' : '-top-[25%] sm:-top-[18%] md:-top-[14%] lg:-top-[14%]'} left-1/2 transform -translate-x-1/2 w-full h-auto z-50 drop-shadow-xl pointer-events-none`}
51+
className={`absolute ${isFullPage ? '-top-[42%] sm:-top-[40%] md:-top-[50%] lg:-top-[43%]' : '-top-[25%] sm:-top-[18%] md:-top-[14%] lg:-top-[9%]'} left-1/2 transform -translate-x-1/2 w-full h-auto z-50 drop-shadow-xl pointer-events-none`}
5252
preserveAspectRatio="xMidYMid meet"
5353
>
5454

@@ -87,8 +87,8 @@ const BrightCandleView: React.FC<BrightCandleViewProps> = ({ fftData = [], betaP
8787
</svg>
8888
<div className={`absolute bottom-0 w-full ${isFullPage ? 'h-full' : 'h-[30%] sm:h-[32%] md:h-[34%] lg:h-[36%]'} bg-gradient-to-b from-gray-100 to-gray-200 dark:from-stone-600 dark:to-stone-700 rounded-t-md backdrop-blur-md shadow-xl before:absolute before:inset-0 before:bg-white/10 before:opacity-40 before:rounded-b-xl before:rounded-t-md`}>
8989
<div className="absolute inset-0 overflow-hidden rounded-t-md bg-gradient-to-b from-cyan-300 via-blue-400 to-gray-900">
90-
<div className="absolute inset-0 flex items-center justify-center">
91-
<div className="text-sm sm:text-xl md:text-xl lg:text-2xl font-semibold text-gray-700 px-2 sm:px-3 py-1 ">
90+
<div className={`absolute inset-0 ${isFullPage ? 'flex flex-row justify-center pt-10' : 'flex items-center justify-center'} `}>
91+
<div className="text-sm sm:text-xl md:text-xl lg:text-3xl font-semibold text-gray-800 px-2 sm:px-3 py-1 ">
9292
{Number.isFinite(betaPower) ? String(Math.floor(betaPower)).padStart(2, '0') : '00'}
9393
</div>
9494
</div>

src/components/FFT.tsx

Lines changed: 51 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -380,78 +380,99 @@ const FFT = forwardRef(
380380
const ctx = canvas.getContext("2d");
381381
if (!ctx) return;
382382

383-
canvas.width = container.clientWidth;
384-
canvas.height = container.clientHeight;
383+
const containerWidth = container.clientWidth;
384+
const containerHeight = Math.min(containerWidth * 0.6, 500);
385+
canvas.width = containerWidth;
386+
canvas.height = containerHeight;
385387

386-
const width = canvas.width - 20;
388+
const width = canvas.width;
387389
const height = canvas.height;
388390

389-
const leftMargin = 90;
390-
const bottomMargin = 50;
391+
ctx.clearRect(0, 0, width, height);
391392

392-
ctx.clearRect(0, 0, canvas.width, height);
393+
// Unified padding and spacing logic
394+
const padding = Math.min(width, height) * 0.08;
395+
const yAxisLabelOffset = 30;
396+
const leftMargin = padding + yAxisLabelOffset + 20;
397+
const rightMargin = padding;
398+
const topMargin = padding;
399+
const bottomMargin = padding + 40;
393400

401+
// Axis color and font setup
394402
const axisColor = theme === "dark" ? "white" : "black";
403+
const fontSize = width < 640 ? 12 : width < 768 ? 14 : width < 1024 ? 16 : 18;
404+
const labelFont = `${fontSize}px Arial`;
405+
const titleFont = `${fontSize + 2}px Arial`;
406+
407+
ctx.strokeStyle = axisColor;
408+
ctx.fillStyle = axisColor;
409+
ctx.font = labelFont;
395410

411+
// Draw axes
396412
ctx.beginPath();
397-
ctx.moveTo(leftMargin, 10);
413+
ctx.moveTo(leftMargin, topMargin);
398414
ctx.lineTo(leftMargin, height - bottomMargin);
399-
ctx.lineTo(width - 10, height - bottomMargin);
400-
ctx.strokeStyle = axisColor;
415+
ctx.lineTo(width - rightMargin, height - bottomMargin);
401416
ctx.stroke();
402417

403418
const freqStep = currentSamplingRate / fftSize;
404419
const displayPoints = Math.min(Math.ceil(maxFreq / freqStep), fftSize / 2);
420+
const xScale = (width - leftMargin - rightMargin) / displayPoints;
405421

406-
const xScale = (width - leftMargin - 10) / displayPoints;
407-
408-
let yMax = 1;//Default to prevent division by zero
409-
yMax = Math.max(...fftData.flat());
410-
411-
const yScale = (height - bottomMargin - 10) / yMax;
422+
let yMax = Math.max(...fftData.flat(), 1);
423+
const yScale = (height - topMargin - bottomMargin) / yMax;
412424

425+
// Plot lines
413426
fftData.forEach((channelData, index) => {
414427
ctx.beginPath();
415428
ctx.strokeStyle = channelColors[index];
416429
for (let i = 0; i < displayPoints; i++) {
417430
const x = leftMargin + i * xScale;
418431
const y = height - bottomMargin - channelData[i] * yScale;
419-
420432
ctx.lineTo(x, y);
421433
}
422434
ctx.stroke();
423435
});
424436

425-
ctx.fillStyle = axisColor;
426-
ctx.font = "12px Arial";
427-
437+
// Y-axis labels
428438
ctx.textAlign = "right";
429439
ctx.textBaseline = "middle";
430-
for (let i = 0; i <= 5; i++) {
431-
const labelY =
432-
height - bottomMargin - (i / 5) * (height - bottomMargin - 10);
433-
ctx.fillText(((yMax * i) / 5).toFixed(3), leftMargin - 5, labelY);
440+
const yTicks = 5;
441+
for (let i = 0; i <= yTicks; i++) {
442+
const value = (yMax * i) / yTicks;
443+
const labelY = height - bottomMargin - (i / yTicks) * (height - topMargin - bottomMargin);
444+
ctx.fillText(value.toFixed(2), leftMargin - 10, labelY);
434445
}
435446

447+
// X-axis labels (Frequency)
436448
ctx.textAlign = "center";
437449
ctx.textBaseline = "top";
438-
const numLabels = Math.min(maxFreq / 10, Math.floor(currentSamplingRate / 2 / 10));
439-
for (let i = 0; i <= numLabels; i++) {
440-
const freq = i * 10;
450+
const minLabelSpacing = fontSize * 4;
451+
const labelFreqStep = Math.ceil((minLabelSpacing / xScale) / 10) * 10;
452+
const maxFreqToLabel = Math.floor(Math.min(maxFreq, currentSamplingRate / 2));
453+
454+
for (let freq = 0; freq <= maxFreqToLabel; freq += labelFreqStep) {
441455
const labelX = leftMargin + (freq / freqStep) * xScale;
442-
ctx.fillText(freq.toString(), labelX, height - bottomMargin + 15);
456+
ctx.fillText(freq.toString(), labelX, height - bottomMargin + 5);
443457
}
444458

445-
ctx.font = "1.0em Arial";
446-
ctx.fillText("Frequency (Hz)", (width + leftMargin) / 2, height - 15);
459+
// X-axis title
460+
ctx.font = titleFont;
461+
ctx.textAlign = "center";
462+
ctx.textBaseline = "top";
463+
ctx.fillText("Frequency (Hz)", width / 2, height - padding * 0.9);
447464

465+
// Y-axis title
448466
ctx.save();
449467
ctx.rotate(-Math.PI / 2);
468+
ctx.font = titleFont;
450469
ctx.textAlign = "center";
451-
ctx.fillText("Magnitude", -height / 2, 15);
470+
ctx.textBaseline = "bottom";
471+
ctx.fillText("Magnitude", -height / 2, padding);
452472
ctx.restore();
453473
}, [fftData, theme, maxFreq, currentSamplingRate, fftSize, channelColors]);
454474

475+
455476
useEffect(() => {
456477
if (fftData.some((channel) => channel.length > 0)) {
457478
plotData();

0 commit comments

Comments
 (0)