Skip to content

Commit 42ce83c

Browse files
authored
Merge pull request #148 from marcodejongh/log_heatmap_scale
Log scale for heatmap
2 parents bd912dc + 1482bf8 commit 42ce83c

File tree

1 file changed

+14
-37
lines changed

1 file changed

+14
-37
lines changed

app/components/board-renderer/board-heatmap.tsx

Lines changed: 14 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { getImageUrl } from './util';
33
import { BoardDetails } from '@/app/lib/types';
44
import { HoldHeatmapData } from '@/app/lib/db/queries/holds-heatmap';
55
import { LitUpHoldsMap } from './types';
6-
import { scaleLinear } from 'd3-scale';
6+
import { scaleLinear, scaleLog } from 'd3-scale';
77
import useHeatmapData from '../search-drawer/use-heatmap';
88
import { usePathname, useSearchParams } from 'next/navigation';
99
import { useUISearchParams } from '@/app/components/queue-control/ui-searchparams-provider';
@@ -104,50 +104,27 @@ const BoardHeatmap: React.FC<BoardHeatmapProps> = ({
104104
};
105105
}
106106

107-
// Create breakpoints at different percentiles for more even distribution
108-
const percentileValues = {
109-
min: values[0],
110-
p20: getPercentileValue(values, 20),
111-
p40: getPercentileValue(values, 40),
112-
p60: getPercentileValue(values, 60),
113-
p80: getPercentileValue(values, 80),
114-
p95: getPercentileValue(values, 95),
115-
max: values[values.length - 1]
116-
};
107+
const min = Math.max(1, values[0]);
108+
const max = values[values.length - 1];
109+
110+
// Use log scale for better distribution of values
111+
const logScale = scaleLog()
112+
.domain([min, max])
113+
.range([0, HEATMAP_COLORS.length - 1])
114+
.clamp(true);
117115

118116
const getColorScale = () => {
119117
return (value: number) => {
120-
if (!value || value === 0) return 'transparent';
121-
122-
// Map the value to color index based on which percentile bucket it falls into
123-
let normalizedIndex;
124-
if (value <= percentileValues.p20) {
125-
normalizedIndex = (value - percentileValues.min) / (percentileValues.p20 - percentileValues.min);
126-
} else if (value <= percentileValues.p40) {
127-
normalizedIndex = 2 + (value - percentileValues.p20) / (percentileValues.p40 - percentileValues.p20);
128-
} else if (value <= percentileValues.p60) {
129-
normalizedIndex = 4 + (value - percentileValues.p40) / (percentileValues.p60 - percentileValues.p40);
130-
} else if (value <= percentileValues.p80) {
131-
normalizedIndex = 6 + (value - percentileValues.p60) / (percentileValues.p80 - percentileValues.p60);
132-
} else {
133-
normalizedIndex = 8 + (value - percentileValues.p80) / (percentileValues.p95 - percentileValues.p80);
134-
}
135-
136-
const index = Math.floor(normalizedIndex);
137-
return HEATMAP_COLORS[Math.max(0, Math.min(index, HEATMAP_COLORS.length - 1))];
118+
if (!value || value < threshold) return 'transparent';
119+
const index = Math.floor(logScale(value));
120+
return HEATMAP_COLORS[index];
138121
};
139122
};
140123

141124
const getOpacityScale = () => {
142125
return (value: number) => {
143-
if (!value || value === 0) return 0;
144-
// Use percentile values for opacity scaling
145-
return Math.max(0.2, Math.min(0.8,
146-
scaleLinear()
147-
.domain([percentileValues.min, percentileValues.p95])
148-
.range([0.2, 0.8])
149-
.clamp(true)(value)
150-
));
126+
if (!value || value < threshold) return 0;
127+
return Math.max(0.3, Math.min(0.8, logScale(value) / HEATMAP_COLORS.length));
151128
};
152129
};
153130

0 commit comments

Comments
 (0)